Under the Radar

192: Backporting


00:00:00   Welcome to Under the Radar, a show about independent iOS app development.

00:00:04   I'm Marco Arment.

00:00:05   And I'm David Smith.

00:00:06   Under the Radar is never longer than 30 minutes, so let's get started.

00:00:09   So today's topic, we're going to dive into a little bit of talking about backboarding,

00:00:14   which sort of generally is the concept of taking a particular feature or functionality

00:00:20   in sort of a current version, typically of an operating system or an SDK,

00:00:25   and creating that same capability in a previous or older version of this.

00:00:30   And so this is sort of a technique or something you often find yourself having to do

00:00:35   if you want to present a common user experience across different versions of a platform,

00:00:43   but in a way that is good for the user, potentially complicated and annoying for you as the developer,

00:00:51   because rather than sort of just degrading the experience as you go backwards in sort of age,

00:00:57   rather than iOS 13 to iOS 12, there's a new feature in iOS 13 that you want to take advantage of.

00:01:03   You backport that into iOS 12 by essentially recreating either a subset of the functionality

00:01:09   or a full version of the functionality, and you're kind of taking on the responsibility

00:01:13   that the platform vendor typically would take on by doing this.

00:01:17   So it's a tricky thing to do, necessarily want to do, but every now and then,

00:01:21   it is something that I found to be useful and essential in many ways.

00:01:26   And recently I ran into this kind of where I've kind of, in my mind, I'm preemptively backporting some features,

00:01:32   because as we're recording, we're about a month away from WWDC, and so in about a month,

00:01:37   we'll probably get a whole new set of functionalities and capabilities on all the various Apple platforms.

00:01:42   And for me specifically, I'm expecting watchOS 7 is going to include whatever SwiftUI version 2 is,

00:01:51   and that is likely going to include a lot of new functionalities and new controls,

00:01:56   things that are kind of either missing or awkward to do currently, and so I expect that to come,

00:02:02   but in the meantime, it's kind of this awkward thing of do I want to hold off on adding features

00:02:07   or building my own personal controls and capabilities into the app, waiting to see what they're going to do.

00:02:13   And specifically, I ran into a situation where I want to do a lot of mapping in WatchSmith,

00:02:19   and this kind of mapping is something that is just not--there's no built-in controls for this.

00:02:24   On iOS, you have MapKit, and even if you're using SwiftUI, you could use MapKit by wrapping it,

00:02:30   kind of in or out. You can wrap SwiftUI UI kit controls in SwiftUI controls,

00:02:36   and you'll end up with something that is pretty reasonable, but on watchOS, there is no MapKit,

00:02:42   so if I want to do this, I have to build it entirely myself.

00:02:45   And as I was kind of going down the path of do I want to start this project knowing that watchOS 7

00:02:51   may introduce a map control for SwiftUI on the watch, and it'll be great.

00:02:57   And in the end, I kind of went on the, well, that would only be useful to me if I was able to require

00:03:04   watchOS 7 right away, and if I could, then maybe that would be useful waiting for.

00:03:10   And then this would just be a feature that I push off until September.

00:03:14   But as it is, I was like, A, I don't necessarily want to wait until September for this.

00:03:18   I want to get it out sooner than that. And then B was the reality that I feel like we're entering

00:03:22   this kind of awkward period with all of the operating systems and platforms generally,

00:03:26   where we're going to have a much higher degree of backwards compatibility for a while.

00:03:33   I feel like we've talked about it many times that iOS 12 seems like it's going to be lingering for a long time,

00:03:39   at least in a way that we need to be relevant and keep support for.

00:03:44   And I kind of have a feeling that watchOS 6 feels like it might be the same thing.

00:03:48   I wouldn't be surprised if we drop support for the series 1 and 2 watches with watchOS 7,

00:03:55   and if that's the case, then 6 might become the new baseline that everything has to hold onto,

00:04:00   because while the series 1 and 2 watches aren't a dramatic proportion of the user base,

00:04:05   I think it's going to be a similar story to iOS 12, where it's enough that people are going to want to

00:04:11   keep using those watches for a long time.

00:04:14   And so I was like, well, I guess that means that I'm going to have to build this myself,

00:04:18   which is an interesting engineering challenge, and then have this funny expectation

00:04:23   that I might be preemptively backporting this functionality.

00:04:26   That in watchOS 7, I may completely not use my version of mapping,

00:04:33   but anytime I'm in watchOS 6 land, then I have to use my version of mapping.

00:04:39   And so it was just kind of an interesting thing where you have these weird parallel experiences,

00:04:42   because what I don't want to do necessarily is go down the path where features just disappear

00:04:47   when you're on one platform or another, because that's a giant support headache,

00:04:52   where in this screenshot, I see this feature, why can't I see this feature?

00:04:57   And that's a nightmare, and so I tend to try and backport as much as I can,

00:05:01   but the technical implementation of that is certainly not straightforward.

00:05:05   Yeah, and I think it is worth very quickly revisiting why this is important.

00:05:08   As you touched on, these old OSes are sticking around longer and longer than they used to.

00:05:12   Back when the platform was younger, the OSes would move pretty quickly,

00:05:18   people would update pretty quickly, it was a more enthusiast base for many of our apps,

00:05:23   and devices were being replaced more often.

00:05:28   So having to keep around an old device for a long time was less of a concern back in the days of faster update cycles,

00:05:30   younger platforms, much more carrier subsidizing of phone prices,

00:05:38   so pretty much everybody updated every two years because it made no sense not to.

00:05:42   There was all that going on, and in the modern era,

00:05:45   we have devices that stick around for a much longer time.

00:05:50   We have a much larger market, which is good,

00:05:53   but in the sense that now, though,

00:05:56   there's a lot more of the market that's more conservative

00:05:59   or unable to upgrade their device very quickly.

00:06:01   And so if we want to capture decent market share,

00:06:04   or if we don't want to just leave money on the table,

00:06:07   we do have to support older OSes for longer than we used to.

00:06:10   My advice for years was,

00:06:10   indies should never support anything before the current version,

00:06:15   because we don't have time,

00:06:18   we might as well use all the newest modern stuff to our advantage,

00:06:19   and just leave people who don't update in the dust.

00:06:24   And that's just become harder and harder in recent years.

00:06:27   I went through the same thing myself,

00:06:29   where last fall with Overcast,

00:06:31   I immediately required iOS 13 within, I think,

00:06:33   two weeks of it being released,

00:06:37   something like that, within a pretty short time,

00:06:36   and that bit me really hard,

00:06:41   and I angered a lot of people,

00:06:43   and I had to eventually go back and re-add support for iOS 12.

00:06:45   I also had required watchOS 6, same problem,

00:06:50   so I had to go back and re-add support for watchOS 5.

00:06:53   And I stand by those as being the right decisions.

00:06:57   I'm glad I went back and re-added those support,

00:07:00   because I needed it.

00:07:03   And now, as we approach the new OS season,

00:07:04   it certainly raises the question of,

00:07:09   well, do I move it forward this year?

00:07:11   Do I, this fall, do I require iOS 13,

00:07:13   and keep it just one version back like that,

00:07:16   and do I require watchOS 6?

00:07:19   And it is a really tricky thing,

00:07:21   because so often we will get awesome new APIs that we will use.

00:07:24   The good thing is that we are not the only people,

00:07:28   like me and Dave and you are sole theoretical listener out there,

00:07:33   we are not the only people who are facing this problem.

00:07:38   We are not making the only apps out there

00:07:41   where the developers want to use new features,

00:07:43   but they can't, for whatever reason,

00:07:46   drop support for the old OSes quite yet.

00:07:48   And so other people face this problem en masse,

00:07:51   and they make solutions to help with this.

00:07:54   And you know I'm not a huge fan

00:07:57   of using a bunch of third-party code in my app,

00:07:59   but this is one area where third-party code

00:07:59   can actually help.

00:08:04   Almost always, almost every year,

00:08:05   whatever significant new APIs are added to iOS,

00:08:07   there's usually within a couple of months

00:08:11   a third-party library that brings similar

00:08:13   or identical functionality to the previous version of iOS.

00:08:16   So back when they added CollectionView, for instance,

00:08:20   there was, like for previous versions of iOS,

00:08:22   there was a CollectionView class that some developer made,

00:08:24   I can't remember offhand,

00:08:29   and it was almost directly or directly compatible.

00:08:31   And so doing something like that,

00:08:34   I tend to call it a shim,

00:08:37   I think it's a fairly common term for it,

00:08:39   where somebody basically just implements the new API

00:08:41   using the old OS and releases a library

00:08:45   so that you can directly use it.

00:08:47   And you can use the available checks

00:08:49   depending on which one you want to bind to

00:08:49   and everything else, but you can usually directly code

00:08:54   against the new API if it's something that can be done

00:08:56   fairly easily on the old OS and have some library

00:08:59   take care of the old OS version for you.

00:09:02   That doesn't work so well with things like SwiftUI,

00:09:04   where it's just a massive problem domain

00:09:06   that no one's gonna re-implement SwiftUI.

00:09:08   Although, have they?

00:09:12   I haven't actually looked, probably not.

00:09:13   - I know there were some people when it first came out

00:09:15   who I think explored doing that,

00:09:15   in terms of that you could theoretically,

00:09:20   SwiftUI is just a domain-specific language

00:09:23   that describes what you want the UI to look like,

00:09:25   and so you could re-implement it in anything.

00:09:28   And I know people have looked at implementing it

00:09:30   for previous versions of iOS,

00:09:33   as well as even potentially for other platforms

00:09:34   or for the web.

00:09:37   I think someone made an HTML renderer for it.

00:09:38   There are certainly efforts afoot for that.

00:09:41   I think that's an example where I don't think

00:09:40   that that kind of level of backboarding is as useful

00:09:45   because it's such a young and changing,

00:09:48   trying to keep that up to date feels very challenging

00:09:53   and difficult, and if you're going into the world

00:09:56   of SwiftUI, just go into the world of SwiftUI.

00:09:58   But I think it is certainly an effort that some people do,

00:10:01   where you build these shims where you either

00:10:04   transparently or explicitly will support

00:10:06   the same kind of functionality on both platforms.

00:10:11   And I think that's interesting.

00:10:15   I think what's especially interesting is how you build those.

00:10:17   I think it's an interesting technical question of,

00:10:21   do you want those shims to be transparent to you?

00:10:23   Or the shim itself is pretending to be a UI collection view,

00:10:26   and just is on-dudits doing the clever work

00:10:33   to hide itself away on iOS 13 so it doesn't conflict

00:10:38   with Apple's version of UI collection view.

00:10:41   But on iOS 12, it's like, oh, I can totally be

00:10:44   UI collection view and call myself that,

00:10:48   and all your code is going to exactly the same named things.

00:10:50   Which is an interesting thing,

00:10:53   and from a programming perspective,

00:10:55   it means that your core business logic code

00:10:56   is probably cleaner because there's less checks

00:10:59   back and forth.

00:10:58   But from a code smell perspective,

00:11:03   that always makes me nervous when someone else

00:11:06   is implementing something that has UI in front of it,

00:11:08   and then I don't necessarily know exactly which version

00:11:13   I'm dealing with, and you're trying to deal with debugging.

00:11:16   You have a weird behavior on some platform,

00:11:19   you're trying to track down exactly where that is,

00:11:21   and where the root of that is, I don't really love.

00:11:23   And so I tend to prefer the path where I have

00:11:25   somewhat explicit things that are delegating

00:11:30   into a totally separate version,

00:11:33   and I'm just building a layer on top of the shim

00:11:35   and the real thing, and talking to that.

00:11:40   So I prefer to take the approach of doing

00:11:43   a higher-level abstraction that does the delegation for me,

00:11:45   which I don't know if it's actually that different,

00:11:48   but conceptually, for me, I think it's different.

00:11:50   But there is certainly another approach, too,

00:11:52   that you can take for this,

00:11:51   where it becomes almost transparent to you as a developer.

00:11:56   - Yeah, that's the approach that I tend to take.

00:12:00   I feel like if you're looking for guidance

00:12:04   on whether this is a good idea of a kind of thing to do,

00:12:07   you can look at, first of all, just the scale,

00:12:11   the size of what functionality you're trying to make work

00:12:14   on the old OS, if it's not that big of a problem set.

00:12:16   So for instance, iOS 13 added the DiffableDataSource stuff,

00:12:21   and there are lots of libraries to do that

00:12:25   on older versions of iOS, and it's a fairly small

00:12:28   or fairly isolated problem domain,

00:12:32   where you're not having massive parts of your app

00:12:35   intricately involved with some huge framework

00:12:39   to accomplish this.

00:12:41   It's a fairly straightforward API,

00:12:42   and the logic inside that API might be complex,

00:12:42   but it's fairly easy to isolate.

00:12:47   It doesn't touch all of your code,

00:12:49   whereas something like a UI frame or SwiftUI,

00:12:51   having that be what you're trying to mimic

00:12:54   on an older version, it has its tentacles

00:12:57   all throughout your app, and it's much more complex,

00:13:00   and it's much larger of a surface area to have problems.

00:13:03   And that, I feel like, is usually unwise

00:13:07   to do that level of backporting.

00:13:08   If you have a smaller component,

00:13:13   like a DiffableDataSource API,

00:13:15   that's a much easier problem domain,

00:13:17   and it's a smaller, more focused component,

00:13:19   easier to backport without causing direct problems.

00:13:22   And then as for the approach, I think you're right.

00:13:25   I think when you see a direct shim

00:13:27   or a direct replacement where it tries to make it

00:13:31   so that you don't have to think about this replacement exist,

00:13:34   that you can just code against the new API

00:13:33   and it somehow magically works, that is tricky.

00:13:38   And it does have a much higher potential for problems.

00:13:41   - Yeah, 'cause it makes me think of, on Android,

00:13:46   in some ways they work around this with,

00:13:48   they have a much more robust, essentially backporting system

00:13:50   where they have the Google Play services,

00:13:53   which is, in some ways, this first-party shim

00:13:55   that they provide that they let you deal,

00:13:59   there's two versions,

00:14:00   the platform is broken into two parts.

00:14:05   There's the actual OS, which will move forward,

00:14:08   as you would typically imagine,

00:14:11   and then there's the Google Play services,

00:14:13   which is a set of components that Google is backporting

00:14:14   to work on any of the previous versions of the OS.

00:14:19   And in that case, that's kind of an interesting approach

00:14:23   where you can kind of trust that they're doing the hard work

00:14:25   to make sure that it actually works the same

00:14:26   and is a consistent experience,

00:14:31   and that there's no weird bugs

00:14:33   and there's no weird issues across platforms.

00:14:34   They're taking care of that for you.

00:14:36   And if Apple provided something like that,

00:14:39   then that would certainly be interesting.

00:14:41   If they provided a library that we could link to

00:14:43   that added support for some new thing on older versions,

00:14:45   which I'm trying to think,

00:14:50   I can't think of a time that they've ever done that.

00:14:51   There was some feature I have this vague recollection of

00:14:52   that existed in the previous version of iOS.

00:14:57   It existed, but it was private,

00:15:00   and they made it so that you could access it

00:15:02   in a subsequent version of that old version.

00:15:05   I don't know what that was, but that sounds vaguely right to me.

00:15:09   But generally, popovers?

00:15:12   Maybe it was popovers.

00:15:13   There was a thing where it existed,

00:15:16   but we could finally access it.

00:15:18   But typically on iOS, we're just kind of stuck.

00:15:18   This is definitely one of those tricky things,

00:15:23   where pulling in some kind of third-party library

00:15:26   and having it do core major functionality of your application

00:15:30   definitely feels like a much higher, pretty substantial risk.

00:15:37   Certainly it's a risk that is different

00:15:41   depending on the usage distribution of your application,

00:15:43   where if you have lots and lots of people

00:15:48   still running old versions,

00:15:52   then you have to be even more careful.

00:15:54   If this is a diminishing returns situation

00:15:56   where right now you have 10% of your people

00:15:59   on the old platform,

00:16:03   and then you expect it to continue to decline over time,

00:16:04   then dealing with the potential edge cases

00:16:07   or problems or bugs on that platform

00:16:08   is definitely an acceptable risk that you have to take.

00:16:13   But it's definitely a tricky thing.

00:16:16   And sometimes I do wish that Apple did the approach

00:16:18   of having two different things.

00:16:20   There was the OS,

00:16:22   and then there was the functionality part.

00:16:23   And I know they don't need to typically,

00:16:26   primarily because iOS has such good adoption.

00:16:28   But in a world where I feel like their adoption

00:16:31   is slowing down a little bit,

00:16:33   not necessarily for new things that can't support it,

00:16:33   but because they're having devices

00:16:38   that have such long lives

00:16:40   that eventually don't get the new stuff,

00:16:42   that it might be something that they may have to look at

00:16:45   and potentially model after what Google does.

00:16:48   Yeah, honestly, I don't see it happening.

00:16:51   I think you're right that the need for it

00:16:53   is ever increasing as the rate of software updating

00:16:56   slows down for them.

00:16:59   But also it's just so not Apple-style

00:16:58   to give significant support to old OSes at all.

00:17:03   Apple-style is like, "We've moved on, so should you."

00:17:06   Period.

00:17:09   It's a very unfriendly attitude

00:17:10   towards holding onto the old version unnecessarily.

00:17:15   And for people who are forced to hold onto the old version

00:17:19   because the new version doesn't work on their devices,

00:17:22   Apple's position tends to be, "Buy a new device."

00:17:24   So it's very much not a very friendly attitude towards that.

00:17:25   But anyway, we are brought to you this week by, surprise, Linode.

00:17:30   Whether you're working on a personal project

00:17:34   or managing your enterprise's infrastructure,

00:17:36   Linode has the pricing, support, and scale

00:17:38   you need to take your project to the next level.

00:17:40   They have 11 data centers worldwide,

00:17:42   including their newest in Sydney, Australia.

00:17:44   All of them feature their enterprise-grade hardware,

00:17:46   S3-compatible storage option,

00:17:49   and their next-generation network.

00:17:51   So Linode delivers the performance you expect

00:17:52   at a surprisingly good price.

00:17:52   I'm a Linode customer, Dave's a Linode customer.

00:17:57   It's a wonderful service.

00:18:00   I've been with them for eight years now,

00:18:02   and I have all of Overcast hosted there.

00:18:05   It's a wonderful service, a wonderful host,

00:18:07   great value, great performance, great support

00:18:10   when you need it.

00:18:12   You can get started today with a $20 credit.

00:18:13   You'll get access to all of their wonderful features.

00:18:16   They have plans starting at just $5 a month.

00:18:19   All of these servers come with root access, of course.

00:18:18   You can install whatever kind of software you need to install.

00:18:23   They have one-click installs if you want to save some time

00:18:26   for popular things like WordPress,

00:18:28   a LAMP stack, game servers, so much more.

00:18:30   They have wonderful plans to choose from,

00:18:33   all these different general-purpose plans,

00:18:35   plus things like specialized plans for needs

00:18:37   such as dedicated CPUs or GPU compute plans,

00:18:39   high memory plans, and so on.

00:18:42   Go to linode.com/radar and use promo code

00:18:44   under the radar2020 when creating a new account,

00:18:45   and you will get a $20 credit towards your next project.

00:18:50   They're also hiring right now.

00:18:53   If that interests you, go to linode.com/careers

00:18:54   to find out more.

00:18:57   Everyone else, go to linode.com/radar.

00:18:58   Once again, use promo code under the radar2020

00:19:00   for that $20 credit.

00:19:03   Our thanks to Linode for their support of this show

00:19:05   and all of Relay FM.

00:19:07   So something else that I think is interesting

00:19:09   when you talk about backboarding

00:19:10   is perhaps the opposite of it,

00:19:11   which is the forward boarding, I suppose,

00:19:10   which is the situation you find yourself in

00:19:15   where you have an implementation

00:19:18   for some particular feature or capability

00:19:20   in your application,

00:19:22   and then the platform vendor creates

00:19:24   an alternative implementation, essentially,

00:19:26   of that capability, that function.

00:19:29   Those things that you previously had to do

00:19:30   with custom code,

00:19:32   they're providing a standard way to do that.

00:19:33   And kind of thinking through

00:19:36   when you would actually want to move forward to that

00:19:36   or adopt it, and this could be in my case

00:19:41   where I have my version of WatchSmith MapKit

00:19:43   that I've implemented,

00:19:48   and if they introduce their own,

00:19:49   should I move to it or should I deal

00:19:51   with the backboarding issue

00:19:52   where I have just these two parallel versions,

00:19:54   or if I just keep using the old version for a long time?

00:19:56   Or you could also imagine a world where you're just like,

00:19:59   there's a new feature that you need to support

00:20:01   because it's something that people are going to want

00:20:01   to expect, but you might just implement it using the tools

00:20:06   available for you in the old version of the OS.

00:20:09   You could do dark mode without all the cool new

00:20:11   dark mode features in iOS 13,

00:20:14   which is ultimately what I ended up doing.

00:20:17   My dark mode is iOS 12 entirely,

00:20:19   except for there's a few little things where on iOS 13

00:20:22   I'll check the system-wide dark mode

00:20:25   to know if I should switch between light and dark,

00:20:26   but I don't take advantage of any of the new built-in things

00:20:31   where it can automatically switch the color provided

00:20:35   to a control or things for you.

00:20:38   I'm just doing it all the old boring way in iOS 12,

00:20:40   and I can just keep that going,

00:20:43   and at some point maybe it would make sense

00:20:45   to future port that functionality into something,

00:20:46   but typically it isn't.

00:20:49   I imagine, for example, an overcast,

00:20:51   if they added a silence removal thing directly into AVPlayer

00:20:54   as one of the options.

00:20:59   Right now you can change the rate,

00:21:00   and if they just had a Boolean where it's like,

00:21:01   "Remove silences? Yes? No?"

00:21:03   At some point, would you want to remove smart speed

00:21:05   or your implementation if you were able to move

00:21:07   to their own version of it?

00:21:09   Sure.

00:21:12   That's certainly a question,

00:21:14   but do you think it has so many of these things

00:21:16   that they have the advantage of working at such a lower level

00:21:19   that so often their version can be so much better

00:21:20   than anything that we can do?

00:21:25   Well, and also they have access to all of their APIs.

00:21:27   One of the issues that I often face

00:21:30   is trying to develop functionality for the Apple Watch,

00:21:33   and certain APIs are just not available,

00:21:36   even though I know they're there,

00:21:38   but they're not available publicly for developers

00:21:39   in that OS release.

00:21:41   And so Apple can use them, but we can't.

00:21:42   So there are lots of things like that

00:21:44   that Apple could do where we just couldn't,

00:21:45   and a first-party solution would be substantially better

00:21:50   than anything we could make ourselves.

00:21:53   But yeah, and actually the area of audio functionality

00:21:55   and dark mode, those are two things.

00:21:58   I wrote down my examples of things I've shimmed

00:22:00   for compatibility reasons, and dark mode

00:22:02   and audio functionality are the two categories I have here.

00:22:05   I built my dark mode entirely for previous versions of iOS,

00:22:08   because I've had dark mode as an option

00:22:12   in Overcast forever, and so it was very simple for me

00:22:14   to make it 12-compatible again.

00:22:19   All I basically did was add a toggle

00:22:21   at the top of the screen when you're running iOS 12,

00:22:24   like, "Are you in dark mode right now or not?

00:22:27   On or off?"

00:22:29   And in 13, that's just not there,

00:22:30   and it uses the system functionality

00:22:32   for determining whether you should be in dark mode or not,

00:22:33   but the rest of the screen of choosing

00:22:36   what should be your light theme

00:22:38   and what should be your dark theme,

00:22:39   that's the same across both OSes.

00:22:40   In the audio area, I have, on the main app,

00:22:43   there was a new audio unit type

00:22:48   that made certain direct playback methods

00:22:51   that are really close to the hardware,

00:22:54   it made them way easier to make,

00:22:55   that was available on iOS 13 and above only.

00:22:57   I built my newest audio engine against that, requiring that,

00:23:01   but there was a crazy API workaround that you could do

00:23:05   that is pretty hacky and works on iOS 12,

00:23:08   and so I basically have an abstracted,

00:23:13   something I believe called a direct audio unit,

00:23:16   and on iOS 13, it isn't even used.

00:23:18   I can just use the built-in system functionality

00:23:22   for whatever audio unit it is, and it works fine.

00:23:24   On iOS 12, I call my direct audio unit,

00:23:27   but it follows the same API.

00:23:29   Similarly, on watchOS 5 compatibility,

00:23:31   my latest update to the watch,

00:23:35   and yes, I know there are some issues with it,

00:23:34   I'm working on that, but my latest update to the watch,

00:23:39   it had streaming capability,

00:23:42   and to do that, I had to use AVPlayer.

00:23:44   AVPlayer does not work this way on watchOS 5,

00:23:47   it just doesn't work the way I need it to.

00:23:51   On watchOS 5, you had to use AVAudioPlayer,

00:23:54   so rather than trying to shim it

00:23:56   so it's perfect with AVPlayer,

00:23:59   I went with the abstraction model of compatibility shims,

00:23:59   which is I just went a level up,

00:24:04   and I have something called, I'm pretty sure,

00:24:06   watchAudioPlayer on the watch,

00:24:08   and watchAudioPlayer has two subclasses.

00:24:10   One uses AVPlayer, one uses the old AVAudioPlayer.

00:24:14   The AVPlayer one supports streaming,

00:24:17   the other one doesn't.

00:24:18   And so at runtime, I just check to see what you're running,

00:24:19   and I use one, you know,

00:24:22   I'm pretty sure it's just a simple if available check,

00:24:25   I use the modern AVPlayer one if your OS supports it,

00:24:25   and if not, I use the old one,

00:24:30   but they present the same interface to the app,

00:24:32   and so the entire rest of the app

00:24:35   is only calling into that watchAudioPlayer abstraction.

00:24:37   And the great thing about doing the abstraction method,

00:24:40   instead of just trying to like directly provide this API,

00:24:42   is that you can make the abstraction

00:24:45   only do the subset of functionality

00:24:48   that you need your compatibility shim to do.

00:24:50   So you don't have to worry about

00:24:52   all the different possible APIs that might be called,

00:24:52   or that you might inadvertently use

00:24:57   because you don't realize you're dealing with an abstraction,

00:25:00   you know, on the old version necessarily.

00:25:02   No, you can just have, you know,

00:25:04   whatever your parent class is,

00:25:07   your watchAudioPlayer can just expose

00:25:08   like the six methods and properties you actually use

00:25:11   in the rest of your app.

00:25:13   And then you can have a much smaller surface area for bugs

00:25:14   and for weird edge cases and everything else.

00:25:18   So generally, like when providing compatibility shims,

00:25:20   the abstraction method, I greatly prefer

00:25:25   to the whole API emulation method.

00:25:28   Yeah, and I think too what's interesting there

00:25:31   is if you're building, it's a good reminder, I think,

00:25:32   that when you're building your own controls

00:25:36   or your own functionality,

00:25:38   like making it so that if you needed to in the future,

00:25:39   take advantage of the new version of something,

00:25:43   that if you're building good segmented,

00:25:46   good object-oriented programming in general,

00:25:51   adding in that functionality shouldn't be that difficult anyway.

00:25:54   I don't tend to go crazy with building my own custom classes

00:25:58   and wrappers for system functions.

00:26:02   It is certainly something that is interesting,

00:26:04   where if you had started off building

00:26:07   with a watchPlayer class,

00:26:09   and then Apple adds this new thing

00:26:11   and you want to take advantage of it,

00:26:14   if you'd already been building the rest of your app

00:26:13   against a slightly abstracted version of it,

00:26:18   you would have been able to do that

00:26:21   even more straightforwardly.

00:26:22   And so it's certainly something

00:26:23   to keep in the back of your mind

00:26:24   if you feel like this is something

00:26:25   that is going to come down the road,

00:26:28   that you might want to plan ahead a little bit.

00:26:31   Like in my case, I've been trying to keep

00:26:34   a lot of my mapping stuff be very high level

00:26:37   and sort of segmenting out the implementation

00:26:39   from the logical definition of what's happening.

00:26:41   So I have this abstraction layer anyway,

00:26:46   and that's kind of intentional

00:26:48   so that I can switch out the implementation

00:26:50   if I need to and want to,

00:26:53   without having to pull these two things apart.

00:26:55   So it's good, I think,

00:26:59   if you're building something

00:27:00   that you think this might be something

00:27:02   you need to ultimately do,

00:27:03   where you have this back-ported,

00:27:04   forward-forwarded situation.

00:27:06   It's a great situation to make sure you

00:27:08   add in that abstraction layer ahead of time

00:27:07   and preemptively, just to sort of put yourself

00:27:12   in a good position for the future.

00:27:15   Whereas generally, if it's not the situation

00:27:17   where you ever expect that to happen,

00:27:19   like maybe you don't,

00:27:21   you can definitely go crazy

00:27:21   with weird class factories,

00:27:23   and you can go way beyond the point

00:27:24   where it's actually useful.

00:27:27   But if it's a situation where you kind of expect

00:27:28   that you might be needing to do back-porting,

00:27:30   just starting with a nice abstraction layer

00:27:33   I think puts you in a good position.

00:27:32   And I think what I found kind of reassuring

00:27:37   about this experience myself is that

00:27:39   so often this time of year,

00:27:41   like from maybe like March, April to June,

00:27:43   I tend to kind of hold off on implementing new features

00:27:48   because I'm like, "Well, what if Apple introduces

00:27:50   something new and important,

00:27:52   and I have to adopt it, and I'm wasting work?"

00:27:55   And in some ways it's slightly freeing

00:27:58   that I feel like I'm going to have to support

00:27:59   the old versions for a longer period anyway.

00:27:58   And so this doesn't have to necessarily be

00:28:03   this kind of fallow period where I can't get

00:28:05   a lot of new interesting features built.

00:28:08   I'm just like full steam ahead.

00:28:10   I can build stuff,

00:28:12   I can add stuff to my applications,

00:28:13   and I just keep in the back of my mind

00:28:15   I want to keep these things as abstract as possible,

00:28:16   and I want to make sure that I'm doing it in a way that

00:28:19   if I do need to ultimately end up with this back-ported

00:28:21   or more parallel implementation situation in the future,

00:28:24   that I'm in a good place for it.

00:28:29   But I think as long as I go into it with that in mind,

00:28:31   it's a good situation, and in some ways I'm gaining

00:28:33   a couple of months of productive work a year,

00:28:36   which is kind of a cool thing.

00:28:38   Yeah, but I would also caution people against

00:28:41   doing too much abstraction prematurely.

00:28:44   If you don't already need the abstraction,

00:28:47   you probably don't need to write it.

00:28:50   If you can't immediately see,

00:28:50   "Okay, I need to make an audio player

00:28:55   that will work on watchOS 5,

00:28:57   but I want to use the new feature of watchOS 6,

00:28:59   which is this concrete thing I already know about

00:29:01   for this OS that's already out."

00:29:02   That's a fine place to do it.

00:29:04   But don't start abstracting everything in your entire app

00:29:07   because you might someday need it.

00:29:09   That way lies madness and waste.

00:29:11   It's being thoughtful and intentional about that.

00:29:14   In a situation where you expect to do it,

00:29:15   rather than where it's the difference between you might

00:29:20   and you probably will.

00:29:23   - Or you already do.

00:29:25   - Yeah, so if you're in one of those situations,

00:29:26   then for sure, go ahead.

00:29:28   Otherwise, premature optimization

00:29:29   is such a waste of time,

00:29:31   so definitely avoid it if you can.

00:29:33   - And premature abstraction is the root

00:29:35   of all evil these days.

00:29:36   No one optimizes anymore.

00:29:38   Now it's all about premature abstractions.

00:29:39   Thanks for listening, everybody,

00:29:42   and we'll talk to you in two weeks.