PodSearch

Under the Radar

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:00 ◼   ► Yes.

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:50 ◼   ► Bye.

00:29:51 ◼   ► Bye.

00:29:51 ◼   ► [BLANK_AUDIO]