Native App Tips To Save Your Sanity

Andy Trevorah speaking at London Node User Group in January, 2017
57Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

9 months after you’ve started writing a hybrid app, you’re going to stumble across some unexpected native problems that don’t show up in tutorials. This talk will go over these and other oddities that most web developers wouldn’t expect. Talk includes: Classic Android and iOS architecture for restful apps, weird native bugs (with fixes), great things that javascript/web should have


Transcript


- [Andy] Hello, everyone. I'm Andy, as you've heard. This talk is mainly about native apps, but from the perspective of web. So, if you've only used or developed Android or iOS apps with Cordova or React Native, this will hopefully fill in some of the gaps and show you some stuff from the other side. No, I don't want you, Siri. So, I'm going to start with talking about the classic kind of native architecture. So, when you're designing a native app from scratch, this is kind of what you do. Then I'll go into the kind of unknown unknowns, so the weird things you may not know about native that will probably set your project back a few weeks. With solutions, of course, which will be nice. And after all that, all those downers in section two, I'm going to end it with just one nice thing about native. There are more nice things. I just don't have enough time and, you know... So, first of all, why native? You're probably working for some web company, and they've got some idea of, like, "We have this amazing website, but now we need an app." Why do you need an app? Like, it costs money to develop an app and support an app. So, the usual thing that's given is that users with an app installed are more likely to use your company's business more often. Now, that's got a bit of an asterisk because that's a correlation rather than causation. It can happen that people that use your business a lot will actually also install the app just because they like your business. Plus, I mean, you can just install a web app. Like, there's no real justification for this. The other one that's given is push notifications improve retention which is true. When you have a notification come in, and it says, "Hey, your friend Tina has just uploaded a new recipe for sausages," or something, like, you're going to tap on the notification. You want to see Tina's sausages, and, you know, people will start using the app every day. But this is, again, possible with web. Some bits are coming, and even if you don't have push notifications, you can probably make do with emails. So, doesn't really count. Something, something, API. There may be better APIs on native for doing a particular thing, like some kind of accelerometer stuff, maybe, something to do with cameras. But again, you have a website, you're trying to make an app of the website. You probably don't even need that, so it comes to our final thing which is that users actually feel they're getting things done faster with a native app, and it's quicker and easier. That's pretty much it. Like, nothing else really matters that much. Maybe the API stuff, but it's a case-by-case basis. So, how do native apps feel fast, and how do you make your native app feel faster than your website? That's what your customers want. Again, like, we could go into the whole JavaScript, versus Java, versus Objective-C, versus Swift in terms of speed, but all three platforms, they have the same basic architecture of there's an event loop. They're single-threaded. You do most of your work there, and as long as you can get your stuff done in under one-sixtieth of a second, it doesn't really matter. There's not going to be any judder, so it's not really the language. Is it the installed assets? Like, you can install a native app and have all the stuff ready. Like, maybe that gives you a little bit of advantage over web, but honestly, if you have a decent web app, and you preload all your assets, there's like a one-time hit, and that's about it. So, doesn't count. Also, both web and native, they still have to fetch their content over the network, so, like, they still have the same downside there. So, the actual reason why they feel fast is databases. This is kind of a thing that's been with iOS and Android apps from the start, and this is how they're designed. It's this architecture. So, we have our UI controller which is, on iOS, it's a UIViewController. On Android, it's a Fragment or an Activity. This is the stuff that just organises all your views and deals with models, all that kind of stuff. And when it starts, it immediately queries, at our point here, queries the database for its content. That's where it gets all its content from. At the same time, it also starts the API service far on the left. API service does all your network stuff, gets the JSON out, pipes it through a JSON processor. Sadly, it has to be a separate class just because Java, Objective-C, Swift, they all kind of suck at JSON parsing, so you need to unit test it. Just, seriously. Like, it works fine for 90% of stuff, but that last 10% sucks. And then it goes straight into the database which is wrapped with either Core Data for iOS, Content Provider for Android. And that wrapper basically sends out a broadcast to anyone that's listening, saying, like, "Hey, that query, if you're interested in that query, that query has now changed. Please re-query," and you get the up-to-date information. So, let's go through an example. So, you have your app, it's just started. You have your UI controller. It immediately checks the database. Is there anything there? Nope. So, it shows a loading spinner while also still calling the API service. API service then gets all the stuff it needs, puts it through the JSON processor, puts it into the database, and alerts everyone, and, hey, now we have our content. So, this all comes from the database. It means that, at this point, if the app is killed and then restarted, it can load everything back up from the database. It's just the store that's there. We can also do some really cool stuff where...so, we have five items on the screen. Like, if we have, say, 1,000 items on this list, we'd only need to keep 5, maybe 6 in memory. So we can, when you scroll, collect, you know, fetch more stuff from the database and really cut down on memory usage. So, let's do something cool, so let's tap on the top item and assume there's some kind of detail view. So, this new UI controller will do the same thing the previous one did. It'll check the database for what it needs, and, lo and behold, the database has content for that first item. So it immediately shows it. It doesn't have to do any web request. There's no loading. It's just straight there. It doesn't even check whether it should show a 404 or anything. It just immediately shows that content. Still, at the same time, it's going to be using the API service to go get more stuff from the network. Lo and behold, there is more stuff, ends up in the database. The database shouts about it, and we show it. We have the same thing that we had with our first UI controller, which means that we can kill the entire app, relaunch it, all this information will still be there on the database, and we can immediately show this content. This is actually quite a useful thing. Like, you don't normally go about killing your apps, but Android and iOS will kill your app all the time. You may know about it through...it's like memory pressure, so, you know how we have, like, hundreds of apps open, but only, like, three of them are really there. It's even less than that. Android and iOS will kill your app as soon as they can. Android, if your app is in the background, and it's not doing anything, there's no, like, event loop stuff going on, it will just kill your app. The idea is that it's so easy to recreate your app that it doesn't really matter. You can delay the killing a little bit, but usually it's very hard to do, and you're kind of fighting Android. So, yeah, this is the standard architecture. Oh, yeah. So, anything in red, that doesn't actually touch any UI stuff, which means you can then put it in a background thread. This is particularly useful because databases are really good at, like, transactional stuff, so you just pipe it in, and it's all fine. It's also kind of necessary, again, for the JSON processor, the bad JSON stuff, so Android and Java, your UI will stutter if you try and parse JSON in the UI thread on Android, so just please do this. All right. So, now we have our basic app architecture. Let's go into all the weird crap. I don't have enough time to go through everything. I'm going to go as quickly as I can through this, so let's go. Number one, oh, oh, oh, almost. Yeah, mobile radio eats batteries. This is the main cause for your batteries being drained out, is just the mobile radio. As long as it's not, you know, it's not a flashlight app or something like that, or it's like money, bitcoins. So, this is probably the most important thing just because you can have the prettiest, nicest, easiest to use app in the world, but if it drains people's batteries people will uninstall your app. Like, this is a deal-breaker for everyone. So, the mobile radio is a chip in your phone that communicates with local cell towers, and, well, cell towers aren't that local. They're actually quite far away, and so the power usage is quite high. The way that phones deal with that is they have two states for their radios. You have a sleep state and a wake state. Wake state is where you draw all the power. So, when you make a request, it takes a while for the radio to actually wake up. You have this big power spike from when you actually send out a packet. And now the radio, it doesn't know what kind of networking you're doing, and so it'll stay in the high power state for a decent amount of time while it's expecting packets to be returned. All this time, it's draining battery. Even, like, long after the packet's been received, it's still kind of, "Is something else going to come along? I don't know." It's waiting around. So, iOS and Android both have to deal with this in their own special ways. The main thing is coalescing network requests, so push notifications and background syncing, some other processing, anything that's non-essential gets delayed until you actually need the network. So if you open a webpage, odds are Android or iOS will piggyback that and use it to, you know, check for push notifications or stuff like that. It's really cool. I could really go into a lot of detail here, but I don't have that much time, I'm running out. The thing to take away from this is please don't poll. If you're polling, you are firing up the radio, like, every five seconds, your battery is just going to drain out. Instead, use push, just push the data. Let Android pick it up, or iOS pick up the data when it finds it the most efficient time to do it. What else can I fit in? Yeah, like, the other way to make sure you don't do this, because it's really easy to accidentally do, you may have, like, a WebView that accidentally does it, just take your phone and go outside. Use the mobile radio, use the battery, see what happens. Just field test your stuff. You're not going to find this out by having a phone plugged in and using Wi-Fi at your desk. Cool, Android back stack. Oh, this is a fun one. So, this actually involves some participation. Can everyone with an Android phone do this? I just need you to... I'm holding two things. Open the Gmail app. Open the left drawer, just slide it out, and go to the "Important" inbox and then tap on an email. - [Man 1] I've just ran out of battery. - You've just ran out of battery? Okay, cool. When you've done that, can you tap the hardware back button three times? Where do you end up? - [Man 2] Home screen. - Home screen, yeah, cool. That makes sense. That makes sense. Do three things, go three back, it's all cool. What if we do these five things and then go back three? So, open Gmail, open the drawer, go to "Important." Open the drawer, go to regular. Open the drawer, go to "Important," tap on an email and then tap back only three times. Where do we end up? - Two times, it went back. - Huh? - Two times, it went back. - Oh. Okay, if it's two times, good. Basically, it's not really a history. - [Man 3] I haven't even got Gmail. - If this was like a browser history, you'd expect you'd end up maybe here, the "Important" inbox. But in reality, you end up in the home screen. The take-away from this is that Android navigation and web navigation, like the history, the back stuff, it isn't the same. There's a lot of weird navigation things that you might not expect. Like, on Android there's an implied hierarchy when you go up the back stack. You can share codes between iOS and Android and have kind of a common navigation pattern, but your users will find it confusing. It's a short-term solution, not a long-term. Please just, like, relax, let Android and iOS do the navigation for you. It's a lot less code, and it's a little bit easier, and you end up with a better experience. Okay, app reviews. App reviews are the death of me. So, the weird thing is with a native app, whenever anyone wants to install it or use it, they immediately know what everyone else's opinion of it is. And those opinions are usually, well, they will be quite negative, and they genuinely do affect your mental health. Yeah, they will stick around, and you've got to bear in mind that your first app is going to be your shittiest one. Like, genuinely, like, if you're a good developer, then, logically, your first app will be your shittiest one because, well, your first release will be your shittiest one just because every other release on top of that will be improving it. But, yeah, these can really, really drag you down, but there is a good way to handle it. So, all you need to do is, at the start, just before you do your planning, go through your reviews. Go through every single one of them and just tally up what they're saying. They could say, "Oh," you know, "This app is the worst app in the world. Your notifications suck." Just have a tally with things like this, "Notifications, one." Maybe you get two reviews that say, "Oh, navigation is hell, go kill yourself," which happens, genuinely. Then look, you've got two, and by the end of it, you end up with, you may be down for a little bit, but you have, like, all the negativity becomes more of a statistic. And also, you just happen to have a nice load of stats that you can bring into a planning meeting and say, "Hey," you know, "the things that we thought people didn't like? Here's what they actually don't like," and you can base your planning on that. The good thing is that...so, on the Play Store, you can actually reply to reviews. And if you reply to one of these really shitty reviews with, "Oh, I've actually fixed your thing now," they completely turn over. It's amazing. They call you a God. They give you a five-star review. It was the best, genuinely, so I recommend that. Okay, iOS WKWebView. I'm running out of time. Basically, so, WKWebView is the WebView for iOS. WK stands for WebKit. It's meant to be the better one compared to the old UI WebView. You can read up on some of its downsides, but the big one is that at some point it will go blank, and you can't reproduce it on the simulator. That's because WebKit will crash, and there's no stack trace, and there's no obvious error catching to find out when it happens. The only thing that you'll find out is that the URL property, that you don't normally listen to but can, just turns to null, and you just have to reset the URL. The thing to take away from this is that the WebViews and iOS, they don't last long. They will crash. Push notifications, all right. So, push and notifications are two very separate things. Little bit less on iOS, but try and keep them separate in your head. So, push, this is all about the server side stuff. This is kind of important because making a change once you have multiple apps or versions of apps out there, making a change to your server becomes a lot harder. Step one, remember that, so, most tutorials will have a thing of, "Here's the iOS app. Push notifications, send your first 'Hello, world.' This is all great." Remember that if you have a sign out button on your app, you're going to need to have some kind of way of deregistering your push notifications, because you don't want to have, like, private messages coming into an account that's meant to be signed out. What else? Yeah, so, Apple and Google's push APIs are different. I'm assuming if you're making an iOS or Android app for a website, you probably started with iOS, then Android, or Android, then iOS. And you may jump to some conclusions about how the APIs work. Please don't try and do a common abstraction. You won't have time, and they don't really mesh that well. Like, Google's really good for batch processing, so, sending multiple notifications all out at once, Apple, less so. Apple, you have a badge number. Google, like, the way that they handle permissions is different. On Apple, you require several certificates. Yeah, don't do that. Also, keep your payloads small. What you're actually sending out, the JSON, is technically an API, and you can't really version it that well. So, just give the bare minimum that you can to the app, and let the app fill in all the blanks. That'll make your life a lot easier. I'm running out of time. Oh, don't forget non-events, so, if you have a notification for a new message, don't forget to send a push payload for when that message has been read. That way, like, preloading content and all that kind of stuff makes it so much easier. All right, the notifications side. Yeah, so, these are the notifications on Android. And so, if you have the small payload, or even if you have the perfect payload that has all these properties needed for these notifications, you're still going to have to chain web requests. So these images, these avatars, you don't set them with a URL, you set them by passing a bitmap to Android. So it's really tempting to have some kind of thing where you slowly build up the notification as your API responses drip-feed in. Please don't do that. It looks really shit, and it'll take you, like, a day to do. And you think you're really proud of yourself, but then you actually see the notification, and you have something that alerts you, and it says, "Oh, wait, wait." And it's popping around, and it looks really ugly. Just wait for everything to appear, wait for your APIs to all finish and then show the notification. That'll make your life easier. Okay, I have very little time, so the one nice thing, the one good thing about iOS and Android, the whole ecosystem, this is it. Android internationalisation. It's a bit of a hard one to explain. Has anyone ever been on an internationalisation project? Two, three, four. Oh, okay, okay. Did it go well? Was it... Middle, middle. Did it take a sprint? Two sprints? No, no. - [Man 4] A month, a year. - Months, year. Okay, so, for those that don't know, the main drag of internationalisation is that you have to tokenise all of your UI strings, and it's not just a find and replace. You have to deal with things like plural forms. So, like, you have, like, one pizza, two pizzas, zero pizzas, that kind of stuff, strings that are built from other strings. And then you have right-to-left languages. So, English is left-to-right, and all your margins and padding are usually set up to deal with that. To support right-to-left, then you have to flip things round, and you have to make layout changes. It all becomes a bit of a nightmare. So, Android internationalisation is my favourite thing not because of the fact that it can do these things. Like, most frameworks can. It's not special. It is because of this. They actually have it as a compiler warning, so when you just pass, like, a raw string into some UI stuff, it's going to complain. It's going to say, "You're doing it wrong." It's an IDE. This is Android Studio, so, of course, there's a little lightbulb thing that you click, and it does it all for you, so it will extract the string. You just need to give it a decent name and it'll put it in the strings.xml resource file, yeah, as a warning. So you kind of have to go out of your way not to have your project set up for internationalisation, or set up for localisation, I guess. And the same with right-to-left stuff, so, here we have layout-alignParentLeft. In reality, you want to use layout_alignParentStart so it can cope with right-to-left languages, and, like, this is cool. Like, well, for me, this is cool. To better explain it, here are the languages used in Android. English is a very small percentage. By very small, I mean like a third, but you gotta realise that everything else, that big green bit there, that's one billion people. That's a decent market share. If you just make it so that the cost of localising your app to some other region is just the cost of translation, then, like, you can make a lot of money. Especially because translations, when it's just strings, is around about £30 to £40 per language, which is pretty cool. So, yeah, so what is essentially like a linter really does change things. Now, Google is very Google, so, imagine what they would do if they know your analytics for your app, like who uses it, what kind of people from what country. And they know, just by inspecting your app, what languages you actually support, so, of course, they're going to do this. So, on Google Play, when you submit your app, you can actually purchase translations. So, this is a package deal they've given me to...I think it's for 50% of the supported languages. This is actually quite beautiful, because, by definition, I don't really know how good these translations are. So, what's the difference between $106 and $245? Like, I'm not cheap, I'm not going to give $106. Like, you could write an economics paper on this. It's great, but, yeah, that's one kind of nice but Googley thing. And yeah, there are a lot more things that I'd like to go over, but I really don't have time, so I will be in the pub. If you have any questions, you can ask me then. If you're not going to be in the pub, like, you can contact me or send me an email. Do we have time for questions now? - [Man 5] [inaudible 00:28:15]. We can take a couple questions. - No more questions? All right. - [Woman 1] Yeah, who's going to pay for it? - Questions, anyone? Oh, yes. - [Man 6] So, I was going to ask, you know when you said the thing which gives your performance is the database, and that's because you know you've got, like, an Android database or some sort of an iOS database. But then, if you're using web, and say I'm using a service online, say localStorage, I mean, what sort of advantage does, like, a native database give me over that? - Exactly, that's the cool thing, so, once you have IndexedDB which is supported by quite a few browsers, or localStorage... IndexedDB is a little bit better because you can actually perform queries and get, like, lists of things back. The tricky thing is...so I'm going to go back through. Where's that nice little diagram? Almost there, there we go. The tricky thing is actually the stale query broadcast, so what you want is, when any view makes a change to the database, then it goes to all controllers. So, on the web, if one window makes the change, you want the other windows to recognise the change. So, cross-window events aren't really a thing, except for localStorage, so what you can actually do is, you kind of hack around this by having... So, on localStorage you can actually listen to changes, and so any other window can make a change to that localStorage and the other windows will find out about it. So this is pretty much, yeah, totally possible with web. - What we've got are [inaudible 00:30:11]. - Yeah, yes. The other thing that we have to remember is that it's not just the speed of this, it's also the kind of UX patterns that come with it. So, on Android and iOS, there's so much kind of implied knowledge by, you know, the tabs at the bottom. You can switch tabs, and you know they won't perform some kind of destructive action. All this kind of stuff that makes a native app easier to use. Yeah? - [Man 7] What would your suggestion be on unifying the push notifications between iOS and Android? If you're watching, would you say, "If Android, then do this?" Or else using a SaaS? - I would say... Oh, for push or for the notifications? - For push. - For push? - Yes. - I would say, if you're doing iOS or Android, just make it work for one of them and then, once you have that working, maybe start working on another. Because there's stuff like iOS maybe needs different events to listen to. So, on iOS you have the badge number on the app. That may be suited to your own internal events for that. That might not apply for Android. Like, the cost of doing push notifications isn't that high, like the actual infrastructure, so just see what events that you're doing internally are and see what maps. That enough? Cool. - [Woman 2] Cool. Well, thanks, Andy.