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: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: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: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.