PodSearch

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.