View hierarchies in iOS applications have only gotten more complex over the years, particularly as view controller containment has gained traction as a nice way to compose parts of your application together without succumbing to inheritance (and as such, tighter coupling than you probably want). One downside of this is that displaying something on top of the currently displayed view can potentially involve quite a bit of hierarchy traversal and understanding of how your UI’s pieces all fit together.
When attempting to overlay something on top of your entire view hierarchy – common examples include a notification banner or a heads-up display – it’s helpful to move up a level beyond regular views or even view controllers, and start thinking in the context of windows. While one could reach for UIApplication.shared.keyWindow, or employ some private API to find e.g. the window that’s currently hosting the status bar or an alert dialog, a far simpler and cleaner approach is to simply create a new window of your own. This is far from a new technique, but I only recently learned how easy it actually is in practice.
UIWindow inherits from UIView, meaning you can simply create a new instance by providing it with a frame: let window = UIWindow(frame: frame). Magically, you don’t actually have to explicitly add your new window to the view hierarchy. Simply initializing it and setting its isHidden property to false1 will cause it to be shown on screen, with its stack order dictated by its windowLevel property. UIWindowLevelStatusBar allows your window to sit atop everything else.
Keep a strong reference to your newly created window and add whatever subviews you want to it. When finished, simply removing the reference will cause the window (and as such, its subviews) to be removed from the view hierarchy (and subsequently, from memory).
There’s a little bit of work involved in making sure that your new window doesn’t unintentionally impact the status bar style, so I’ve provided some sample code below that shows how to specify exactly how you want it to behave (if you’re not using view controller-based status bar appearance, you can ignore this and continue driving the style however you do currently).
extensionUIWindowfinalclassStatusBarPreferringViewController:UIViewController// MARK: - InputsprivateletstatusBarStyle:UIStatusBarStyle// MARK: - Initializationinit(statusBarStyle:UIStatusBarStyle)self.statusBarStyle=statusBarStylesuper.init(nibName:nil,bundle:nil)}requiredinit?(coderaDecoder:NSCoder)fatalError("init(coder:) has not been implemented")}// MARK: - UIViewControlleroverridevarprefersStatusBarHidden:Boolreturnfalse}overridevarpreferredStatusBarStyle:UIStatusBarStylereturnstatusBarStyle}}staticfuncnewWindow(level:UIWindowLevel=UIWindowLevelStatusBar,statusBarStyle:UIStatusBarStyle)->UIWindowguardletkeyWindow=UIApplication.shared.keyWindowelsefatalError("Must have a key window")letwindow=UIWindow(frame:keyWindow.bounds)window.windowLevel=levelwindow.isHidden=falsewindow.rootViewController=StatusBarPreferringViewController(statusBarStyle:statusBarStyle)returnwindow}}
UPDATE:Streaks has actually added all of the features I wanted since the time of publication. I’m now happily using it exclusively.
We’d all like to be more productive, but it’s far easier to stick with a daily habit like reading for at least five minutes than, say, training to run a marathon sometime in the next year. I’ve personally found these types of small, repetitive goals really easy to gamify, and if your brain is anything like mine, a few strategic dopamine deployments can really go a long way. Drinking eight glasses of water a day isn’t hard to begin with, but it’s certainly a lot easier if you’re constantly being reminded about it. And if you’re made aware that you’re actually at risk of breaking a 15 day streak, you’re even more likely to get off the couch for a refill. Obviously these streaks don’t really matter, but brains work in mysterious ways. Or at least mine does.
It’s really easy to go overboard here. In a perfect world, I’d of course like to read about current events and practice the guitar every day, but the quickest way to make sure you don’t drink those eight glasses of water is to lump them alongside a bunch of other goals that you weren’t realistically going to hit anyway. Take something like hitting a daily stand goal1; I can usually accomplish this without really thinking much about it, and on the days when I don’t, I’m usually only an hour or two short. But since missing a day isn’t particularly crucial, there’s no compelling reason for me to explicitly check how I’m doing. Going from pull (“Let me proactively see how many hours I’ve stood for today”) to push (“Hey, you need three more hours or you’re going to lose your 20 day streak”) provides enough of an impetus to do the task that was easy to begin with as long as you didn’t forget.
Like weather or to-do list applications, there are a lot of different habit trackers, which shouldn’t be surprising given that everyone responds to subtly different types of motivation. Most are pretty similar, however. Before diving in too deep, let’s take a look at a couple that aren’t really full-featured habit trackers, but interesting entries nonetheless.
Sessions and Activity++
Sessions is unique amongst the applications listed here, as it’s entirely built around setting timers for habits. If your goal is to read for five minutes each day, it’ll kick off a timer and increase your streak once it’s completed. It’s very nicely designed, and there are certainly plenty of cases in which timers are useful when performing daily habits2, but it’s not flexible enough to be a general purpose streak tracker.
Activity++ has a narrow focus; it tracks streaks and provides gorgeous visualizations for the three metrics highlighted by Apple’s Activity app – Move (active calories burned), Stand (hours in which you’ve stood and moved for at least a minute), and Exercise (minutes of “brisk activity”) – and does so automatically by integrating with HealthKit. Given the small feature set, Activity++ is a very nice application, but it frustratingly doesn’t notify you if you’re at risk of breaking a streak. There is an Apple Watch complication, but it oddly doesn’t include any streak data. I’d like to see a simple Today widget with streak information added as well.
Next, a group of apps that I found to all be pretty comparable to one another.
Habit List, Momentum, and Strides
These are all nice, and all pretty standard as far as streak tracking goes. They each provide a bevy of flexibility options (multiple times per day or week, every 2-3 days, only on Tuesdays, etc.), so depending on your needs, the specific configuration that one provides might be reason alone to use it. Habit List has my favorite design of the bunch, but Momentum has a Today widget which may give it a slight edge. I don’t personally need a desktop component, but if you do, Momentum has a Mac app and iCloud sync, while Strides (my least favorite of the three) is also available on the web.
With a few slight tweaks, either Habit List or Momentum could be sufficient, but they’re each missing at least one of the two features that I’ve since come to rely on from…
Productive and Streaks
Productive is similar to the apps outlined above, feature-wise, and sports an incredibly custom UI that I really enjoy using. But the ingenious difference is that it lets you specify when in the day you plan to perform each of your habits: in the morning/afternoon/evening, or anytime. It also let’s you indicate which specific hours your morning, afternoon, and evening are comprised of3.
Why? If you only perform a certain habit (e.g. flossing) at night, there’s no need for your homescreen to show a badge all day indicating that you haven’t done it yet. I’m pretty conservative about app icon badges – I think important ones should be noticeable and jarring, which means not letting unimportant ones dilute their prominence. Productive lets me do exactly this, which makes up for the fact that it doesn’t offer a Today widget or a Watch complication.
The easiest habits to track are the ones that get automatically tracked for you. While Streaks allows you to enter custom habits as well, it’s main draw is a wide variety of built-in presets for automatically tracking based on your HealthKit data: sleeping, drinking water, weighing yourself, exercising, etc. For this reason alone, tracking your health-based habits with Streaks is a no brainer. It even has a Today widget, a Watch complication, and really nice custom Watch notifications.
The biggest problem with Streaks is that it supports a maximum of only six habits. This, combined with the aforementioned smarter badging solution that Productive employs, has me using both in conjunction with one another: Streaks for habits that can be automatically tracked via HealthKit, and Productive for everything else. And it’s working really well for me.
I don’t realistically expect Productive to add HealthKit support, or Streaks to drop its habit limit and ape Productive’s badging implementation, but if one or the other did, I’d happily consolidate.
Productivity is all about finding a system that works for you, and importantly, not trying to do too much. In my case, the combination of reminders and streak tracking helps keep me motivated and prevents me from forgetting, making it easy to keep up with simple habits that I know would otherwise lapse. As our smartphones and watches learn more and more about ourselves, the opportunities to employ systems4 that help us be better versions of ourselves is only going to increase.
There’s been a lot of talk these past couple of years about how good Apple is or isn’t getting with regards to developing web services. When referring to Apple’s services acumen, I think people generally mean the speed, consistency, and reliability of offerings like iMessage, iCloud, Apple Music, and the App Store(s).
I’d argue that these characteristics, while necessary, are not sufficient for a web service to truly be “great.” To make a great service, the proprietor must truly understand and buy into one of the web’s core tenets: interoperability. This is only becoming more important as time goes on.
We shouldn’t think of the “web” as only what renders inside a web browser. The web is HTTP, and the open Internet.
I really like the sentiment here, but I worry about whether or not Apple sees it this way. HTTP, a universal protocol that allows clients to talk to servers in ways that wouldn’t be possible if everyone used a different (perhaps proprietary) transport mechanism, is only as good as the number of clients that are actually allowed to communicate with your server using it.
CloudKit web services is one of Apple’s more recent service endeavors, and the fact that it can be openly communicated with over HTTP is reason for optimism. But let’s take a look at Apple Music. Unlike most other streaming music services, Apple Music doesn’t have a public HTTP API, meaning a third-party like Last.fm can’t easily integrate with it; there simply isn’t a way for the Last.fm servers to connect with Apple’s the way that they can and do integrate with Spotify. iOS 9.3 did add client-side Apple Music APIs, but these aren’t nearly as useful as an HTTP API would be. Nobody’s servers run on iOS.
Apple Music works with Sonos speakers, but it doesn’t work with the Amazon Echo. And no one outside of a Cupertino business development meeting will have the ability to make it work with the next great Internet connected speaker or smart home device. I buy the argument that the new web is “HTTP and the open Internet,” but in this case, Apple might as well be using something proprietary instead of HTTP. The lack of openness means they aren’t actually allowing their service to be a part of this new web1.
Apple now claims that being a services company is important to them. If they’re able to address the latency and reliability issues that their services have historically been plagued with, they may have succeeded at exactly what they set out to improve. But I still personally won’t consider them a good services company until they take tangible steps towards making their APIs far more open than they have been to date. These types of companies understand that they alone cannot build all of the interactions their users would find useful (nor would targeted, limited partnerships suffice). They earn the adoration of their developer community by empowering them to create the next big app or feature, standing on the shoulders of giants rather than sitting in their pocket.
In addition, I’d love to see this same mentality adopted by third-party client-side application developers, not merely Apple. In 2013, Brent Simmonsastutely opined that when his customers asked him for “sync,” they were usually really asking for multiple versions of an application. Users of his Vesper app for iPhone wanted an iPad version and/or a Mac version; the fact that an HTTP API running on a server would facilitate these new versions was an implementation detail, not what anyone was explicitly asking for.
While I think he was right in 2013, I’m not positive that this same logic holds today. For the apps and services that I rely on the most, I increasingly do care about how the back-end can be communicated with. Products like IFTTT, the Echo and Google Home will only proliferate this mentality to more and more non-technical consumers. They may not know about REST or HTTP, but they’ll wonder why their Echo can hail an Uber but not a car from a different car service. Interoperability will become more of a selling point as the Internet-of-Things becomes more of an actual thing.
Their answer to products like IFTTT and the Echo is Mail Drop, a service that turns emails that into OmniFocus actions. While I’m heartened that they clearly care about interoperability and plan to continue making Mail Drop more advanced, I’m flummoxed as to why they wouldn’t give us a service based on HTTP rather than email3.
Closed systems have enabled Apple (and members of their developer programs) to deliver many of the user experiences we know and love, but past performance does not equal future success. While embracing interoperability might require a philosophical shift away from what has worked to date, I worry that the alternative is Apple continuing to stretch themselves thinner and thinner as software continues to eat the world and hardware continues to become smaller, cheaper, and more ubiquitous.
The Echo, programmable Dash buttons, and now a Siri “API”4, are only the beginning. As the number of Internet-connected devices that we use increases, so does the importance of third-party developers being able to integrate these devices and services without their proprietors having the foresight or granting us specific permission to do so.
Enabling this is what being good at web services means to me.
To me, one of most glaring indications that Apple Music isn’t a “service-first” product is the fact that there isn’t a shared “Next Up” queue that persists across all of your devices. If I’ve started playing an album on my Mac, I should be able to walk out the door and pick up right where I left off on my phone. Rdio did a masterful job of this (even letting you choose to either transfer playback to your phone or simply use it as a remote for your computer), and I really miss it. ↩
Incidentally, Brent also works on OmniFocus now. ↩
It’s only a matter of time until I build my own server that simply turns incoming HTTP requests into outgoing emails to Omni, but this isn’t ideal. My proxy server will have to return a 200 OK for every request, regardless of if an action was successfully created on Omni’s server or not, for example. ↩
Don’t even get me started about how much of a mistake I think it is that Siri integration is entirely client-side as opposed to server-side. Farhad Manjoo (Can Apple Think Outside the Device?) and M.G. Siegler (Siri in Chains) perfectly dissect this decision in their respective pieces. ↩