177: Operating in a Hostile Environment
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:10
◼
►
So we are in the tail end of, I guess, big update season,
00:00:15
◼
►
and it's a time of year where one of the things--
00:00:18
◼
►
we've talked about many aspects of what this looks like
00:00:20
◼
►
and of the changes that come and how to manage new APIs
00:00:24
◼
►
and adopting things and dealing with backwards compatibility with old devices,
00:00:29
◼
►
but I think an area that I think is useful for us to kind of talk about
00:00:33
◼
►
is the ways in which sometimes our apps are changing underneath of us,
00:00:39
◼
►
or there are updates or things that are changed inside of iOS or WatchOS
00:00:44
◼
►
or whatever operating system you're working on
00:00:46
◼
►
that can then cause problems for application
00:00:49
◼
►
where ostensibly code that hasn't changed, unrunning things,
00:00:53
◼
►
can potentially be turned around in iOS updates and cause lots of problems.
00:00:58
◼
►
And it can manifest itself in a lot of different areas that we'll kind of talk through.
00:01:02
◼
►
But I think it's an area that, like this year in particular,
00:01:06
◼
►
I think there was a lot of this where iOS 13, WatchOS 6 had a lot of--
00:01:12
◼
►
there were a lot of changes in some of the lower-level parts of the system
00:01:17
◼
►
that, at least for my applications and I think for yours as well,
00:01:20
◼
►
there's been just issues.
00:01:22
◼
►
And some of these are user-facing.
00:01:24
◼
►
Some of these are things that are indirectly user-facing.
00:01:27
◼
►
There's been a lot of changes in the way that when your app is backgrounded
00:01:32
◼
►
how likely it is that it will remain in the background
00:01:34
◼
►
versus being terminated and then relaunched,
00:01:37
◼
►
which is sort of user-facing and sort of not.
00:01:40
◼
►
In your case, if someone's listening to audio and they get killed,
00:01:44
◼
►
the user would be aware.
00:01:46
◼
►
But even just otherwise, if the user has to, when they tap on your app icon,
00:01:51
◼
►
if it relaunches with a full relaunch versus just immediately restoring,
00:01:56
◼
►
it makes the app feel more responsive.
00:01:58
◼
►
It makes it feel good.
00:02:00
◼
►
But being aware even that your app is being killed in the background
00:02:02
◼
►
is something that is sometimes awkward to notice
00:02:05
◼
►
or something that's hard to see.
00:02:07
◼
►
I mean, especially a lot of these behaviors are really tricky
00:02:10
◼
►
when they don't really happen when you're connected via Xcode
00:02:13
◼
►
to your application because your app is rarely killed in the background
00:02:17
◼
►
while it's in an active debug session.
00:02:19
◼
►
I imagine the app is kind of given a different level of priority
00:02:25
◼
►
in that context than it would if it was just running
00:02:28
◼
►
and then you go off and do other stuff.
00:02:30
◼
►
And so these are often kind of tricky to do.
00:02:32
◼
►
So I think there's an interesting thing to just kind of talk about,
00:02:34
◼
►
A, identifying these things.
00:02:36
◼
►
What are some of the kind of pitfalls that are likely to cause problems
00:02:40
◼
►
when things change in iOSes?
00:02:42
◼
►
And then just kind of some of our experiences and then some of the tools.
00:02:46
◼
►
I think Apple has increased dramatically some of the tools
00:02:49
◼
►
that we have at our disposal, and the organizer especially,
00:02:52
◼
►
to identify these kind of behaviors
00:02:54
◼
►
and then so we can kind of respond to them accordingly.
00:02:57
◼
►
- Yeah, exactly.
00:02:58
◼
►
I mean, and I've been facing so many issues like this.
00:03:01
◼
►
And the thing with issues like this where the system is terminating your app
00:03:05
◼
►
for some reason or just because it wants the memory back for its own purposes,
00:03:10
◼
►
many of these issues are mostly out of your hands.
00:03:14
◼
►
Many of these issues are not your fault, but many of them are.
00:03:18
◼
►
And it's often hard to know the difference.
00:03:21
◼
►
And it's hard to necessarily even know that you're having these problems in your apps.
00:03:25
◼
►
Because for the longest time, there was either minimal reporting on this
00:03:30
◼
►
or it wasn't being reported at all.
00:03:33
◼
►
And even if you use a third-party crash reporter framework,
00:03:38
◼
►
I think those still mostly don't get things like energy log terminations
00:03:42
◼
►
and stuff like that.
00:03:43
◼
►
So you really have to look in the organizer in Xcode
00:03:46
◼
►
and look at the crash logs there and the energy logs there
00:03:50
◼
►
to really have an idea of how bad of a problem do you have with these things
00:03:54
◼
►
and why and when are you getting killed.
00:03:58
◼
►
And unfortunately, like, you know, iOS since the very beginning
00:04:02
◼
►
has had, as part of the public API, has had these low memory warnings
00:04:07
◼
►
where on every view controller you get that method that's like,
00:04:11
◼
►
you get notified if the system's on memory
00:04:13
◼
►
and then you have a chance to release some memory.
00:04:16
◼
►
And if you don't release enough memory, your app's probably going to get killed.
00:04:19
◼
►
There is no equivalent to that for tons of other conditions
00:04:26
◼
►
that can get your app killed.
00:04:28
◼
►
And it seems like in every major version of iOS,
00:04:32
◼
►
they seem to be adding more of these.
00:04:34
◼
►
And watchOS also, as you are very well aware,
00:04:37
◼
►
you know, watchOS from the very beginning has had a lot of, like,
00:04:39
◼
►
super tight restrictions on things like how much CPU time your app can use,
00:04:43
◼
►
how many seconds it has to, before it has to, like, you know,
00:04:47
◼
►
draw its snapshot or whatever.
00:04:49
◼
►
There's all these different conditions like that where not only are most of these restrictions
00:04:54
◼
►
undocumented and changed between OS releases,
00:04:56
◼
►
normally in the direction of getting more strict and having more restrictions,
00:04:59
◼
►
but also there is no public API, there's no notification,
00:05:03
◼
►
there's no callback, there's nothing that serves as a warning signal to your app
00:05:08
◼
►
so that you can, like, change what it's doing before it just gets killed by the system.
00:05:13
◼
►
So you really have to dig through these logs to even find out that you are getting killed for this reason.
00:05:18
◼
►
And a lot of times the logs will reveal what the limit is,
00:05:23
◼
►
but sometimes they won't really reveal, like, how you hit that limit or why or what your app was doing.
00:05:28
◼
►
They try to have stack traces on some of them, but they're often not very helpful
00:05:33
◼
►
because it isn't, like, an actual crash.
00:05:36
◼
►
All they can do is, like, sample, like, what it happened to be doing at the moment it decided to terminate the app,
00:05:41
◼
►
but that oftentimes is not related to why it was terminating the app.
00:05:45
◼
►
And so it's very, very hard to figure some of these things out.
00:05:49
◼
►
And the tooling, in some ways, like the Xcode organizer,
00:05:54
◼
►
especially in the new, in Xcode 11, there's this new metrics tab,
00:05:59
◼
►
and this is super interesting.
00:06:01
◼
►
Like, if you haven't taken a look at the organizer in a while,
00:06:03
◼
►
open up Xcode 11, go to the metrics tab,
00:06:06
◼
►
and it reports all sorts of great, useful information about the versions of your app
00:06:12
◼
►
that have been submitted to the App Store,
00:06:14
◼
►
and over time, how things like battery usage and memory usage and launch time,
00:06:19
◼
►
how those have changed over each release.
00:06:21
◼
►
So you can see, like, for instance, one of the metrics here is disk writes.
00:06:26
◼
►
If your app writes too much to the disk, as of iOS 13, it gets killed.
00:06:31
◼
►
I believe that's even new to iOS 13.2 or 13.1.
00:06:35
◼
►
But if you just write too much to the disk in a certain period,
00:06:38
◼
►
again, these limits, I don't think, are documented anywhere,
00:06:40
◼
►
which is a common problem with Apple these days,
00:06:42
◼
►
but I don't think there's any likelihood of doing this,
00:06:45
◼
►
but if you write too much to the disk, you get killed.
00:06:47
◼
►
And I was having this issue with a couple versions ago.
00:06:51
◼
►
I was updating the search index to Overcast too frequently,
00:06:56
◼
►
and it was causing a lot of disk writes,
00:06:58
◼
►
and I started getting killed during the early 13.2 betas,
00:07:01
◼
►
or whatever this was, 13.1,
00:07:04
◼
►
and I had to figure out, okay, well, how do I write to the disk? Less.
00:07:09
◼
►
And it's kind of hard when someone's downloading a bunch of,
00:07:12
◼
►
when they're downloading a bunch of stuff to their phone,
00:07:15
◼
►
it's kind of hard to avoid writing to the disk to the things they're downloading.
00:07:18
◼
►
But I could look at things like search indexing and say,
00:07:20
◼
►
all right, well, that's something I have control over,
00:07:22
◼
►
so I can at least reduce that.
00:07:25
◼
►
And so I submitted new versions that reduced search indexing,
00:07:28
◼
►
and sure enough, they did.
00:07:30
◼
►
I could see right in the graph on the metrics tab in the organizer,
00:07:33
◼
►
I can see the disk writes went way down after that update,
00:07:37
◼
►
and hopefully down to a point where I'm getting killed less.
00:07:40
◼
►
And so there's all sorts of tooling here, you know,
00:07:43
◼
►
some of which is helpful, some of which is not.
00:07:46
◼
►
Man, I sure wish that we would have more of this information accessible
00:07:51
◼
►
in the app at runtime before getting killed.
00:07:54
◼
►
And there actually is,
00:07:56
◼
►
there was a good article on NS Hipster this past week,
00:07:59
◼
►
there actually is a metrics kit, I believe it's called,
00:08:02
◼
►
that gives you access to some of this stuff at runtime,
00:08:04
◼
►
but it doesn't give you, as far as I can tell,
00:08:06
◼
►
it doesn't give you things like the warnings about, like,
00:08:09
◼
►
hey, you're going to get killed in a few seconds
00:08:11
◼
►
for using too much CPU time.
00:08:13
◼
►
Like, that's something, like,
00:08:14
◼
►
I would love to have a notification for that
00:08:17
◼
►
so I could do things like, hey, you know what,
00:08:18
◼
►
if I'm currently doing something optional that's burning CPU,
00:08:21
◼
►
just stop doing it, you know, stuff like that,
00:08:23
◼
►
stuff like, you know, watch trans codes and search indexing
00:08:25
◼
►
and stuff like that.
00:08:26
◼
►
Like, I do all sorts of crazy stuff,
00:08:28
◼
►
like if I think you're plugged into power
00:08:29
◼
►
and can afford the hit, but if you don't pay attention
00:08:34
◼
►
to this kind of stuff, you'll get reports from people
00:08:37
◼
►
just basically saying, like, hey, your app crashed
00:08:39
◼
►
while I was using it,
00:08:40
◼
►
or your app keeps crashing in the background
00:08:42
◼
►
and I don't know why.
00:08:44
◼
►
And there's plenty of reasons your app can be killed
00:08:48
◼
►
in the background that Apple doesn't really tell you
00:08:49
◼
►
much about, that at least the ones they will tell us about,
00:08:52
◼
►
we can at least do something about that
00:08:53
◼
►
and pay attention to these metrics
00:08:54
◼
►
and start looking at the logs and try to do something
00:08:57
◼
►
that might help this out.
00:08:59
◼
►
- Yeah, 'cause I think the thing that is,
00:09:00
◼
►
other than previously, ultimately,
00:09:02
◼
►
which I feel I found like I would have to do before,
00:09:05
◼
►
without having access to this kind of metrics,
00:09:07
◼
►
there's a little bit of just you are getting
00:09:10
◼
►
the sort of the hearsay reports version of this,
00:09:13
◼
►
which is just someone is reaching out
00:09:16
◼
►
via customer support or whatever
00:09:17
◼
►
and you're just kind of noticing a trend,
00:09:19
◼
►
'cause often these things aren't immediately obvious.
00:09:21
◼
►
Like, there are some that just are.
00:09:23
◼
►
Like, I recently, especially with working with SwiftUI,
00:09:26
◼
►
there's a bunch of things where like,
00:09:27
◼
►
between watchOS 6 and 6.1,
00:09:29
◼
►
there were some like dramatic changes
00:09:31
◼
►
in how SwiftUI rendered.
00:09:33
◼
►
And like, all of a sudden my UI was just broken,
00:09:36
◼
►
you know, on 6.1.
00:09:38
◼
►
So I had to like rewrite it and change it.
00:09:40
◼
►
And like, those kinds of things are great in some ways.
00:09:43
◼
►
Like, while I'm not excited that SwiftUI is still young
00:09:48
◼
►
and constantly changing, it is nice that like,
00:09:51
◼
►
here's this very definitive thing that a user will write in
00:09:53
◼
►
and say like, you know, this label is truncated.
00:09:57
◼
►
And like, on 6.0 it wasn't, on 6.1 it is.
00:10:00
◼
►
And like, it's the same code running.
00:10:02
◼
►
Like, great, that is a nice, easy to identify problem
00:10:04
◼
►
that is like, you know, I can deal with that.
00:10:07
◼
►
What was often tricky with a lot of these kinds of issues
00:10:10
◼
►
is where they're so amorphous that you're just kind of
00:10:14
◼
►
looking for these kind of general trends.
00:10:16
◼
►
Like, is, you know, say you have some operation
00:10:20
◼
►
that only happens on app launch.
00:10:22
◼
►
Like, there's a certain server call, say,
00:10:24
◼
►
that only happens on app launch,
00:10:25
◼
►
or typically happens on app launch,
00:10:26
◼
►
and you're seeing more of those calls to your server
00:10:30
◼
►
after an iOS update than maybe there's something to do
00:10:33
◼
►
with the app being launched in the background,
00:10:35
◼
►
or things like, I remember doing this kind of detective work before,
00:10:39
◼
►
but it's nice to have increased visibility into this.
00:10:42
◼
►
And one thing I will say too that I love about
00:10:45
◼
►
the new metrics that we have here
00:10:47
◼
►
is that it breaks down by like different device types.
00:10:51
◼
►
You can, as well as by like app version,
00:10:55
◼
►
so you can also see, like, I tried to make a change
00:10:57
◼
►
and, you know, to affect, you know,
00:10:59
◼
►
it should improve something.
00:11:00
◼
►
How does it affect it on a relatively new like 10s phone
00:11:04
◼
►
versus how does it detect it on a 5s phone?
00:11:07
◼
►
And like you can get a sense of how the impact is
00:11:10
◼
►
between those things, and also just I kind of love that,
00:11:12
◼
►
it's like, I think my favorite one of these new metrics
00:11:15
◼
►
is launch time, which isn't particularly what we're talking about
00:11:18
◼
►
in terms of like changes to iOS,
00:11:20
◼
►
but just is a new metric that came as part of these
00:11:22
◼
►
new things that are useful for other things,
00:11:24
◼
►
is like getting an actual real world sense
00:11:26
◼
►
of what the launch time is in seconds
00:11:28
◼
►
for a typical user on average for different devices
00:11:31
◼
►
is tremendously useful, because launch time, I think,
00:11:35
◼
►
is one of those things that is so hard to quantify yourself,
00:11:40
◼
►
because it's so dependent often on user,
00:11:43
◼
►
the actual user's data and their device
00:11:47
◼
►
and what else they're using,
00:11:48
◼
►
and there's lots of things that can go into launch time
00:11:51
◼
►
that you don't have control of or that are hard to simulate,
00:11:54
◼
►
and so it's really cool to know that, you know,
00:11:56
◼
►
like for on a 5s, my launch time is three or four seconds,
00:11:59
◼
►
but on a 10s, it's half a second,
00:12:01
◼
►
and like that's really cool to know.
00:12:03
◼
►
But it is interesting to play this detective game,
00:12:06
◼
►
I will say, that you kind of are often kind of looking for
00:12:08
◼
►
what is this trend, what is this thing that's happened,
00:12:11
◼
►
and obviously sometimes you'll know that there are obviously
00:12:14
◼
►
announced changes that are coming in iOS,
00:12:16
◼
►
but often there aren't,
00:12:17
◼
►
and you just kind of have to play this game of looking at it
00:12:19
◼
►
and be like, huh, between this and this version,
00:12:22
◼
►
what's changed and how is this,
00:12:25
◼
►
how is my system responding to it
00:12:27
◼
►
and what are some things that I'm doing
00:12:29
◼
►
that could be questionable, because often,
00:12:32
◼
►
I mean, it's like you said,
00:12:33
◼
►
sometimes this is the system's fault
00:12:35
◼
►
and sometimes it's our fault, like sometimes I feel like
00:12:37
◼
►
we can get away with things that maybe we shouldn't be,
00:12:40
◼
►
but, you know, we're relying on either a bug in the OS
00:12:44
◼
►
or just something that it is not enforcing.
00:12:47
◼
►
I think for a long time there was a bunch of things where,
00:12:50
◼
►
I think we've started to see a couple where there's calls
00:12:53
◼
►
that if you didn't make them on the main thread,
00:12:55
◼
►
you would see a log in Xcode that says,
00:12:58
◼
►
you're making this call not on the main thread,
00:13:02
◼
►
this behavior is not supported,
00:13:04
◼
►
in future versions this will fail,
00:13:06
◼
►
which is a really interesting thing to see
00:13:08
◼
►
when you see it, it's like I like when I see
00:13:11
◼
►
those kinds of messages, that it's like,
00:13:12
◼
►
I'm doing something that does technically work right now,
00:13:15
◼
►
but shouldn't work, and so it's like,
00:13:18
◼
►
I can be proactive and take advantage of those,
00:13:21
◼
►
and any time I see, like, I mean, I loved in,
00:13:24
◼
►
see in Core Data there's a thing where you can set it
00:13:26
◼
►
to be like thread-aware, and if you make any calls
00:13:31
◼
►
to things that are on the wrong thread,
00:13:33
◼
►
like, you know, your manage object context
00:13:36
◼
►
is being accessed from the wrong thread,
00:13:38
◼
►
you can have the debugger sort of essentially
00:13:41
◼
►
catch it and you'll hit a break point and say like,
00:13:44
◼
►
you're doing something bad here,
00:13:45
◼
►
I think there's a similar thing that you can do
00:13:47
◼
►
for accessing UIKit, not from the main thread,
00:13:50
◼
►
that you can, it can sort of prop up,
00:13:52
◼
►
essentially act as an exception in those cases,
00:13:55
◼
►
'cause I think those are often those kinds of edge case
00:13:57
◼
►
things that will work differently between versions of iOS,
00:14:02
◼
►
where you'll suddenly get caught out,
00:14:04
◼
►
that you were doing something that you weren't aware
00:14:06
◼
►
was problematic, but was problematic,
00:14:08
◼
►
but then, you know, it's like, it used to work,
00:14:11
◼
►
but suddenly it doesn't, like, I think I had one of these
00:14:13
◼
►
with iOS 13 where I was presenting a modal view controller,
00:14:17
◼
►
I think, and in certain circumstances it could be presented
00:14:20
◼
►
from a background thread if it was coming from a notification
00:14:24
◼
►
and that notification happened to come from a background thread,
00:14:26
◼
►
like, there was, you know, this sort of chain of events
00:14:28
◼
►
that could have happened where this was happening,
00:14:30
◼
►
and it worked on iOS 12, like, it would present
00:14:33
◼
►
and I never noticed, but on iOS 13 it would fail and crash,
00:14:36
◼
►
and, like, that was just one of those things that I was,
00:14:39
◼
►
I wasn't, you know, doing, I wasn't doing the appropriate call
00:14:42
◼
►
to send it, you know, to dispatch it back to the main thread,
00:14:45
◼
►
but, you know, now in 13 I was very aware of that
00:14:50
◼
►
and had to fix it.
00:14:51
◼
►
- Yeah, I've had, I've been bitten by a few of these things,
00:14:54
◼
►
I know about a year ago, or maybe a little more,
00:14:56
◼
►
I had a problem where if you, I had, you know,
00:15:01
◼
►
I had extensions running for my app and I had my main app
00:15:04
◼
►
and so I thought, hey, why don't I put the SQLite database file
00:15:08
◼
►
in the shared app container, that way the extensions
00:15:11
◼
►
and the main app can all be reading from the same database file
00:15:14
◼
►
and write into it and everything, and SQLite is good about,
00:15:16
◼
►
like, cross process, you know, synchronize access,
00:15:19
◼
►
so I knew that would be okay, so I thought, great,
00:15:21
◼
►
this should be fine, and I was getting all these weird crash reports
00:15:26
◼
►
and what turned out to be the reason was that if you are holding on to a,
00:15:31
◼
►
I believe a writable file handle in a shared app container,
00:15:36
◼
►
when your extension or main app gets suspended or terminated
00:15:41
◼
►
in the background, iOS will kill the app and make a crash log saying,
00:15:46
◼
►
hey, you actually crashed, like, as opposed to just a regular termination,
00:15:49
◼
►
and it took me forever to figure out why because that thing about, like,
00:15:54
◼
►
not having writable handles open in a shared app container,
00:15:57
◼
►
I don't think that was documented anyway, and it took me, I think,
00:16:02
◼
►
weeks to figure that one out and eventually ship an app update
00:16:04
◼
►
that just doesn't have the SQLite file there anymore
00:16:06
◼
►
and just communicates through other methods.
00:16:08
◼
►
For lots of reasons, don't put your SQLite file in the shared app container
00:16:12
◼
►
between extensions and your app, and so that one bit me pretty hard.
00:16:17
◼
►
I've also gotten hit in more recent times.
00:16:21
◼
►
Recent OSes, I believe they started with 12,
00:16:24
◼
►
but at least it's certainly present in 13, are very aggressive about
00:16:28
◼
►
if you open a background task and you don't close it,
00:16:33
◼
►
or if they call your expiration handler and you don't end the task
00:16:37
◼
►
in the expiration handler, they will kill the app with a crash,
00:16:41
◼
►
and that's especially coming up for a lot of people now with iOS 13
00:16:45
◼
►
because iOS 13 significantly reduced the amount of background time you have.
00:16:49
◼
►
It used to be about three minutes, and now I believe it's 30 seconds,
00:16:53
◼
►
something like that, and so the background task API
00:16:57
◼
►
is dramatically more aggressive in iOS 13,
00:17:00
◼
►
so we're seeing a lot of crashes from that,
00:17:02
◼
►
to the point where now, I did this a few months back.
00:17:05
◼
►
I actually changed all of my begin background task calls
00:17:08
◼
►
to use my own custom wrapper library that automatically will close the task
00:17:14
◼
►
if the calling code doesn't, just because there's nothing about my app
00:17:19
◼
►
that will break horribly if it gets suspended unexpectedly,
00:17:23
◼
►
so it's no big deal if the system suspends it,
00:17:26
◼
►
and I didn't cancel the background task,
00:17:29
◼
►
so now I had to actually make this wrapper class just to ensure that I can't do that.
00:17:35
◼
►
And of course, I audited all the ways I was using it too,
00:17:37
◼
►
and I hope I never actually need that fallback,
00:17:39
◼
►
but it's always good to have something like that.
00:17:41
◼
►
All sorts of stuff like that where the system is getting more and more strict
00:17:45
◼
►
over time, and a combination of Apple's engineering strictness,
00:17:50
◼
►
basically of throwing the problem back in our face,
00:17:53
◼
►
and their extreme lack of documentation on any of these things,
00:17:58
◼
►
and the fact that the conditions always change,
00:18:00
◼
►
make it so that we really have to be on our toes,
00:18:02
◼
►
and I don't think things necessarily should be this way,
00:18:05
◼
►
but they are, and we really have to be on our toes all the time,
00:18:08
◼
►
and every OS release, even a dot release,
00:18:12
◼
►
like even going from iOS 13.1 to 13.2, say,
00:18:15
◼
►
even dot releases change these requirements,
00:18:18
◼
►
and add more strict checking on things and everything.
00:18:21
◼
►
You never know when an OS release is going to all of a sudden
00:18:24
◼
►
start killing your app for a transgression that you weren't even aware of,
00:18:28
◼
►
or that used to not be a problem, and now is a problem.
00:18:32
◼
►
We are sponsored this week by Linode.
00:18:35
◼
►
With Linode, you can instantly deploy and manage an SSD server in the Linode cloud.
00:18:39
◼
►
You can get a server running in just seconds,
00:18:42
◼
►
with your choice of Linux distro, resources, and node location.
00:18:45
◼
►
It doesn't matter if you're working on your first server,
00:18:48
◼
►
or deploying a huge complex system, Linode is where to go.
00:18:52
◼
►
They have the fastest hardware network,
00:18:54
◼
►
with outstanding customer support if you ever need help,
00:18:56
◼
►
and it's super easy to launch a Linode cloud server.
00:18:59
◼
►
They now have block storage available in Newark, Fremont, Dallas, Atlanta,
00:19:02
◼
►
Frankfurt, London, and Singapore, soon to be released in Tokyo.
00:19:05
◼
►
Version 4 of Linode's REST API is out of beta,
00:19:08
◼
►
and includes an officially supported Python CLI.
00:19:11
◼
►
It's such a great host.
00:19:12
◼
►
I've got to say, Dave and I have been there forever,
00:19:15
◼
►
and I've been there for something like eight or nine years.
00:19:17
◼
►
All of Overcast is hosted there.
00:19:19
◼
►
I just absolutely love working with Linode.
00:19:21
◼
►
It's been a rock-solid host the entire time I've been there,
00:19:23
◼
►
and it's been the best value that I've been able to find in the market as well.
00:19:27
◼
►
So check it out.
00:19:29
◼
►
And also right now, Linode is hiring.
00:19:31
◼
►
If you want to learn more about that, go to linode.com/careers.
00:19:34
◼
►
Otherwise, for everyone else, check out their pricing options.
00:19:36
◼
►
They have options to suit everyone.
00:19:38
◼
►
Plans start at one gig of RAM for just $5 a month,
00:19:42
◼
►
and they have all sorts of other plans above that for various needs,
00:19:44
◼
►
including high-memory plans.
00:19:46
◼
►
And Linode has a special offer for you.
00:19:49
◼
►
Listeners of this show can go to linode.com/radar
00:19:52
◼
►
and use promo code radar2019 to get $20 towards any Linode plan.
00:19:57
◼
►
So on the one gig of RAM plan, that could be four months free.
00:20:00
◼
►
And with a seven-day money-back guarantee, you have nothing to lose.
00:20:03
◼
►
So give Linode a try today.
00:20:05
◼
►
That's linode.com/radar and promo code radar2019.
00:20:09
◼
►
To learn more, sign up and make the most of that $20 credit.
00:20:13
◼
►
Our thanks to Linode for their support of this show and all of Relay FM.
00:20:17
◼
►
Something that I found kind of interesting in this general topic too recently,
00:20:21
◼
►
I feel like Apple is increasingly using the crash termination
00:20:26
◼
►
as a way to enforce policy.
00:20:29
◼
►
That there was a term early in my career I'm working on iOS,
00:20:34
◼
►
I feel like crashes were usually directly my fault.
00:20:39
◼
►
I did something that was bad.
00:20:42
◼
►
Or in a more modern case, you could imagine in Swift,
00:20:45
◼
►
where if you use -- any time you use the exclamation point operator,
00:20:49
◼
►
you're asking for a crash because inevitably somehow that optional
00:20:54
◼
►
is going to turn out to be nil and it's going to crash.
00:20:56
◼
►
That was just your fault. That's just bad programming.
00:20:59
◼
►
But there weren't as many of these kind of situations
00:21:03
◼
►
where things are being -- policy is being enforced with a crash.
00:21:06
◼
►
But it's now, for example, if you make any privacy-related call
00:21:11
◼
►
inside your application and you don't include the appropriate
00:21:16
◼
►
P list entry with the privacy description text,
00:21:20
◼
►
your app will just immediately terminate.
00:21:23
◼
►
In some ways you could think, "Well, why doesn't the call just fail?"
00:21:26
◼
►
or something like that. It's like, no, Apple enforces that by a crash,
00:21:30
◼
►
by this hard, very aggressive version of that.
00:21:34
◼
►
And many of these resource constraints are enforced in the same way,
00:21:39
◼
►
that there is this very harsh, strong --
00:21:43
◼
►
you do something incorrect, your app is terminated,
00:21:47
◼
►
rather than just getting a not-authorized message
00:21:52
◼
►
or dealing with -- you can imagine softer versions of this.
00:21:56
◼
►
And in some ways, I will say, as a developer,
00:21:58
◼
►
there are parts of me that really like it, in the sense that
00:22:01
◼
►
if I'm doing something that I shouldn't be,
00:22:04
◼
►
often it is for lack of understanding about how to use something correctly,
00:22:09
◼
►
or I don't realize that I'm making a call that will actually
00:22:13
◼
►
deal with any of the privacy APIs.
00:22:15
◼
►
There are situations where things like that can happen.
00:22:18
◼
►
And in some ways it's nice to very strongly get my attention,
00:22:22
◼
►
because nothing gets your attention quite so strongly
00:22:24
◼
►
as the app immediately terminating.
00:22:27
◼
►
That's much more obvious than just the silent failure case.
00:22:33
◼
►
- It's like warnings versus errors.
00:22:35
◼
►
If your app crashes when you do something,
00:22:38
◼
►
you can't just delay fixing that for some future better time
00:22:42
◼
►
that will never come.
00:22:43
◼
►
You have to deal with that.
00:22:44
◼
►
It's forcing you to deal with it.
00:22:46
◼
►
- And in some ways, that's kind of nice.
00:22:50
◼
►
It's a little tricky when they happen in the between-the-
00:22:51
◼
►
It's tricky when they happen in the change in behavior,
00:22:58
◼
►
like the same code now crashes rather than just silently failing.
00:23:02
◼
►
That can be awkward when it happens,
00:23:04
◼
►
and especially when it happens in point updates
00:23:07
◼
►
or more minor things that are harder,
00:23:09
◼
►
like we don't have as much time with,
00:23:11
◼
►
or we don't necessarily look at as much.
00:23:13
◼
►
Because as much as Apple will usually provide us with a developer beta
00:23:17
◼
►
most at least of the major point releases,
00:23:21
◼
►
maybe not the minor point releases, ahead of time.
00:23:24
◼
►
It's also kind of tricky to necessarily always be running those.
00:23:27
◼
►
By all means, run any of the major versions.
00:23:29
◼
►
When iOS 13 came out at WWDC, I have it on a device,
00:23:32
◼
►
and I'm running my apps on it on day one
00:23:34
◼
►
and looking out for these kinds of things.
00:23:36
◼
►
But it's a little tricky for some of the times for the point releases.
00:23:38
◼
►
But I do kind of see where they're coming from,
00:23:40
◼
►
and I kind of like that this generally seems to be a new pattern
00:23:44
◼
►
that they're kind of heading towards,
00:23:46
◼
►
and if you do something wrong, like you were saying with some of the handlers
00:23:49
◼
►
and things where you kind of could have gotten away with it before,
00:23:52
◼
►
and they were a little bit more loosey-goosey about it,
00:23:55
◼
►
it's a different kind of model that I think ultimately
00:24:00
◼
►
will probably encourage higher general compliance and code quality.
00:24:05
◼
►
And as a philosophy, I'm not totally against,
00:24:09
◼
►
because it kind of necessitates this much more rapid improvement.
00:24:13
◼
►
But it's just awkward where sometimes they're happening
00:24:17
◼
►
for things that you don't understand.
00:24:19
◼
►
And I do kind of like, and in some ways it makes sense,
00:24:21
◼
►
that by making it a crash, there's the broadest amount of instrumentation
00:24:27
◼
►
possible for collecting information about that.
00:24:30
◼
►
Like, crash reporting, collecting crash reports
00:24:33
◼
►
is something that Apple does, that third-party frameworks do,
00:24:36
◼
►
that TestFlight does.
00:24:38
◼
►
By turning it into a crash, there's lots of instrumentation
00:24:42
◼
►
and kind of workflows around collecting that information
00:24:45
◼
►
and kind of knowing what happened that is already existing.
00:24:50
◼
►
And I mean, it's kind of funny in some ways that many of these policies
00:24:53
◼
►
are then enforced by the weird little paragraph in the middle of the crash report.
00:24:58
◼
►
Which is like, I don't think was necessarily like that section
00:25:02
◼
►
is kind of supposed to be this communication mechanism back and forth
00:25:06
◼
►
between the system and developers.
00:25:08
◼
►
But it is now.
00:25:10
◼
►
And there's a lot of information where I'll open up a crash report
00:25:12
◼
►
and there's this little paragraph in the middle.
00:25:14
◼
►
It's like, well, the watchdog process found that your application
00:25:18
◼
►
was using CPU for 2.04 seconds and the limit is this,
00:25:22
◼
►
and so you were killed after this amount of time.
00:25:24
◼
►
And we're having this little conversation about why I was killed.
00:25:28
◼
►
And that's interesting and that's useful.
00:25:30
◼
►
And I'm okay with it in some ways,
00:25:32
◼
►
but it is just something that I've noticed recently,
00:25:34
◼
►
that there's this trend towards you do something wrong
00:25:37
◼
►
and your app is killed rather than you do something wrong
00:25:39
◼
►
and it kind of silently fails.
00:25:41
◼
►
And overall I like it, but it is something that I think definitely to be aware of.
00:25:45
◼
►
And probably also just kind of something I've had to make peace with in a good way
00:25:50
◼
►
is for a long time my goal was to always have like zero crashes.
00:25:55
◼
►
Like if you open the analytics tab in iTunes Connect, say,
00:25:59
◼
►
in just like the crashes area, for a long time my goal was always to kind of get the crash rate down to zero.
00:26:06
◼
►
But with a lot of this kind of change, well it used to be in some ways.
00:26:11
◼
►
I've been over a year zero. I think I have like a thousand a day.
00:26:14
◼
►
Yeah, and I think I'm now at peace with that.
00:26:17
◼
►
Like the point I'm heading towards is that it used to be a reasonable goal.
00:26:20
◼
►
Now it isn't, and now it's more I think the crash,
00:26:23
◼
►
like there is telling you things sometimes that you have control over,
00:26:27
◼
►
sometimes that you don't.
00:26:28
◼
►
And the crash is more like a feedback mechanism.
00:26:31
◼
►
And you want overall, like if you see the crash rate going up,
00:26:34
◼
►
it's probably a bad thing. If you see it going down, it's probably a good thing.
00:26:37
◼
►
But the goal is not necessarily to get it to zero.
00:26:40
◼
►
The goal is for it to kind of slowly decrease over time.
00:26:43
◼
►
And if you see any like abrupt jumps in your crash rate,
00:26:47
◼
►
there's probably a lot of interesting logs with lots of paragraph text in them
00:26:50
◼
►
telling you things that you, new policies that you may not have been aware of,
00:26:54
◼
►
or just things that you need to be, you know, that need your immediate attention
00:26:58
◼
►
and that the system is telling you, you need to fix this right now.
00:27:01
◼
►
Yeah. And I think ultimately, I think using crashes this way from Apple's point of view,
00:27:06
◼
►
it makes sense to use it as like an enforcement mechanism for lots of things.
00:27:12
◼
►
Especially it makes sense, you know, as you mentioned how the tooling is so good,
00:27:15
◼
►
it makes a lot of sense for like line by line misuse of something.
00:27:20
◼
►
So like if you are literally like calling an API wrong or something,
00:27:24
◼
►
like that makes tons of sense.
00:27:26
◼
►
Where I think it needs more support around it is when it's about
00:27:30
◼
►
like general conditions being exceeded.
00:27:32
◼
►
You know, things like memory, CPU usage, disk rights,
00:27:36
◼
►
like where there is some limit that the system is enforcing
00:27:39
◼
►
and your app is just going to slowly approach it until it hits it and then get killed.
00:27:43
◼
►
That is where a crash report is pretty much useless to you
00:27:48
◼
►
because who knows where all the memory was going before the one line of code
00:27:52
◼
►
they happened to sample when they made their report.
00:27:54
◼
►
And so it's much more useful in cases like that to have a warning API first.
00:28:00
◼
►
To have something, some callback, some notification, some handler,
00:28:04
◼
►
something where the system tells you like, "Hey, you've reached 80% of this limit."
00:28:09
◼
►
Something like that before it kills you.
00:28:12
◼
►
Because then even if most apps never respond to that, fine,
00:28:16
◼
►
then it will be like the way it is now for them, although they will eventually get killed.
00:28:20
◼
►
But for those of us who want to do better, who want to avoid crashes
00:28:23
◼
►
and who might be doing optional work, please give us the support
00:28:28
◼
►
to actually respond to these conditions before our apps get killed.
00:28:32
◼
►
Because when they get killed, for resource usage basically,
00:28:38
◼
►
it's nearly impossible for us to do anything about that.
00:28:41
◼
►
Like what are we supposed to do with that information besides,
00:28:43
◼
►
"Well, I guess we'll try to use less CPU."
00:28:45
◼
►
How? Where? We don't know.
00:28:47
◼
►
But if we have a feedback mechanism before we get killed,
00:28:50
◼
►
we can add our own logging, we can respond to it, we can turn things off.
00:28:54
◼
►
It's so much better for everyone if we have that.
00:28:57
◼
►
Yeah, and it's also great when that is something that we can simulate.
00:29:01
◼
►
As well. Like, in the same way that we have the network link conditioner
00:29:04
◼
►
that can simulate network conditions, like being able to launch the app and say,
00:29:07
◼
►
"Launch me as though the system is under constraint right now,
00:29:11
◼
►
and let me see what will happen and how I'll be terminated
00:29:15
◼
►
and what behaviors I should expect."
00:29:17
◼
►
And anytime they give those kinds of tools to us,
00:29:20
◼
►
I find them invaluable to just get a sense of where...
00:29:24
◼
►
Otherwise you're doing these weird things that you're like,
00:29:27
◼
►
"Let me launch six other apps while I'm launching my app
00:29:30
◼
►
and start playing 4K video in the hopes that I'm creating a condition
00:29:33
◼
►
that will make my app be resource constrained."
00:29:38
◼
►
Which is not repeatable and is not particularly reliable either.
00:29:42
◼
►
Yeah, and there's some tooling around that, but it's very minimal.
00:29:45
◼
►
More would be a lot better.
00:29:47
◼
►
Anyway, thanks for listening, everybody, and we'll talk to you in two weeks.
00:29:51
◼
►
[BLANK_AUDIO]