Asynchronous & Offline magic tricks on React Native

0 0

Oh okay hi everyone super excited to be talking today I'd like to start off by thanking to whoever these club mattes which are really tasty we don't have those in France or in the UK so today I'm going to talk about making your react native app work offline and it's something that is not ready by default and you probably need to spend some time thinking about how you could implement and how far you want to go with that so I'm going to hopefully give you some magic tricks to to make that easier so I'm woody I work at a company called Kyoto what we do is that we build digital products that fix the business problems that our clients face can be small clients it can be bigger clients to startups corporates and we need to be able to provide them with solutions that can be implemented quickly and offline apps are not that quick to implement at the moment so let's go back in time let's start the talk by going back in time in the different eras of client expectation when building an app so this was the can you get the page to appear faster era so you had usually something like a PHP full stack application and what you wanted to do is to make the browser request to the server as fast as possible so you had to the developer spend its time creating database indexes caching stuff compressing stuff so that the HTML gets quicker to the browser so this was there was basically no need for front-end real work to to satisfy the client then there was the era of jQuery and stuff like that where the client was pissed off because widgets that used to work don't work anymore so you spent your time as the developer finding widgets you find you spend your time fixing regressions because you had trouble fixing and testing your GS code and then we have this error which is way nicer for us because there's a real room for front-end development you can write cool react native and react components you do some fancy state management so there's real intelligence behind it and you just expect that the app is always going to work the way it does on your computer which is not the case and the client could say something like oh it doesn't work on the subway and you said it was going to work so what's going on so this is the problem that we're facing today some made up stats just to explain why it's important to actually think about offline issues I put up some stats of where you might use a mobile app so could be the dentist your dentist waiting room where you have app or 3G connections we need to think about that because the connection is maybe not that good then you have your office toilet where you spend a little more time you have great Wi-Fi usually there so it's not a problem you have an awful tinder data restaurant where you have 4G so then again it's fine you spend a lot of time doing that but then there's a big one the subway and for instance you don't know what's going to happen with that the stats are totally made-up it doesn't even add up to hundreds with its basic you what I do so busy knowing that you need to take care of that what are the steps because we want to build something that is progressive and to me an app must be able to handle failure it's going to happen even if you do everything that I'm going to present to you it will eventually fail so you need to have a way to handle these cases and then you want to aim at avoiding failure so those are two different strategies one is to me mandatory the other is you go as far as you can so must handle failure has been talked about by for instance the company 37signals to created Ruby on Rails and they coined the term defensive designer and the idea is that for each feature you want to implement you want to handle three states the expected state which is you'd probably what you spend your working on there's the loading state that is there when the data is not yet in the app and there's the blank state and the blank state can be you have no data it can be you have no network it can be there's been a problem with the server and you need to really think about the UX and the UI of all these states and the deployment features and then there's should avoid failure which is optional you can go as far as you want depending on the budget and the time you want to spend on it and the idea is you want to decrease the probability of the user facing a state where the app is not functioning in an optimal manner so there's two different aims which is what I wanted to start with that basically we're going to go through two main sections it's going to be reading like getting data on your app and there's going to be writing and this is if you want to make it match the HTTP words reading is guess and writing is everything else post puts patch deletes whatever and you want ideally to have those being handled in an offline fashion and find solutions for that let's start with reading strategies so reading strategies as I said we want to focus first on handling failure and defensive design for fetching strategies can be as simple as putting activity indicators when you have a screen and react native you just put a loader you have then more evolved strategy the Facebook strategy here is to show content that looks like the final layout so that when the data gets here it's not going to phase out the user is going to feel like the content hasn't really moved and for long requests you're going to want to have something that shows progress or at least like distract the user postman has a nice little loader which puts some random sentences it's something that can be quite fun you can put jokes or Easter eggs there the idea is to make the user wait and know that something is happening so this is the basic loading States management that you want in your app the blank state now you also have some problematics you want to fill the space you don't want to have an empty app so for instance Foursquare tells you in a very nice way that you don't have friends and so the idea is you want to save the day you want to provide a call to action to the user so that you can get out as quickly as possible from the blank state and go back to the normal state of the app and then there's a second strategy which is used by for instance Google Chrome which is hijacking the guy one you wanted to go to stack overflow it doesn't work that you can play with a dinosaur and it's fine so those are the two main strategies that you can do for handling blank States so once you've done this defensive design then you can move on to more fun stuff which is to make it work of fun and to basically reduce the probability of having these loading and blank State what's amazing is that with Redux you have already some sort of persistence in the store so you get data from the server does this work I guess yeah you get data from the server and then it gets put in the Redux store and just using redox you have foreground persistence which means that when you move in the app by default the data is still there and you also have persistence when the guy just hides the app and resumes it which is already something interesting and it's by default with Redux if you want to look very quickly at the reducers what you want to do is have you want to listen to requests actions to success actions and earlier actions pre-standard and the idea is that if he wants your data not to be erased when the guy returns to to the screen you don't want to actually erase for instance here we're fetching Trello boards to an app I made and you don't want to erase the boards when you have the request action being received you just want to update the error and the fetching flags and it's this is a very common pattern in Redux and it's interesting because it's so it also links to the defensive design I mentioned which is you have an optimal state which is looking at the boards you have a blank state which is in that case the error boolean and you have the fetching state you fetching boolean which matches the the loading State and by doing that even if you request the data the boards are not going to disappear from your screen and you're going to it's not have an impact the same for the failure for instance where you're not erasing the boards either you're just updating the boolean error next up if you want a bit further you can you want to have something that is persisted when you kill the app and the easiest way to do that is you use some of you probably know it's it's Redux persist the idea is that the read of storage you have is going to be synced with usually deicing storage on react native it can be local storage if you're doing a react app or any other storage that you want and so the store is persisted and once the app is restored it's the store is rehydrated from assing storage and by default does that with the whole store obviously you can have some per reducer mechanism to rehydrate your your store or do some different with it and then you may run into some issues and classic issues when you want to process stuff is tithe size issues for iOS for instance the limits of things you can store in the ethnics point is actually the piece that you have on the iOS phone but maybe you don't want your app to fail your phone with data for Android it's text megabytes by default but you can change that but you you have to be aware that if you store a lot of things it made me into problems you have some small libraries I'm going to go into detail with that that help you to solve these issues Redux process transform compressed allows you to basically compress the strengths that are in the store you can win some pretty good improvement and then you could pass it to to show it on screen for instance and Redux versus transform filter allows you to basically filter your states when you want to persist it for instance maybe you're only showing a title and you don't want to store a blob image blob that you get when you do the request from the server and by using dabs you're going to not store everything in the server while being able to use the generic rest api that some other clients might want to have the blob of data or whatever um next up you want to be able to handle updates I know that for some of you guys have seen the talk before talking about code push and stuff like that you want to have as many updates as you want if you're agile and this means that your store might change over time for instance in that simple case you had just data who's holding the held in your store and you want to add the fetching flag the fetching boolean in here and this means that when you update the app the store is actually when it gets rehydrated it's not going to hold the data that will allow your app to display the data and so there's something pretty cool that's called Redux persist migrate the IDs you store some sort of a version in your store that allows you to execute migrations just like if you were a back-end developer which is pretty cool in that case I'm just if I'm using currently version 1 and when I do an update version 2 is available it's going to modify my state to store in a different way the dating apps field and add in the fetching flag with a default value and that's why the app when it gets updated is still going to be persisted you have to do another request even though it wouldn't be a big deal if you need to make a request when you update yeah it's just an example so once you want once you've done that the idea is you want to warn the user that the data they see is not the freshest data available and you want to warn the user of that and you can see Facebook what they're doing is that when you scroll up it shows a little no internet connection and sales so you can still post while offline which is a bit of a spoiler of the writing part because Facebook allows you to do write actions while being offline an easy way to implement that is react native provide the net info on its core library and you can just listen to any connectivity change and it that way you have the boolean is connected you could dispatch your redux action to just update something in your store and show this little you a UI element when the data is the network can't be reached so this is an easy solution to do that so to finish up with the reading part of the talk very simply you want to you may want to optimize further which is what I was talking about when traveling back in time to make the request get there faster this is mostly back-end work you have solutions for that like varnish for instance or memcached for those of you who don't know what it does basically if someone made the request five minutes ago it's not going to do the whole database computations querying that might be heavy and make the request the data get slower in your app it's just going to reuse the data because it server I stored in the RAM as you could implement very quickly some varnish or something like that to to improve the speed at which you get from the loading States to the optimal State let's move on to something that is probably more complex and less done in absolute think that a lot of you have implemented in some way some caching for reading data writing it's not that common and again we want to apply the same strategy we want to start with handling failure and for that it's starting defensive you can just warn the user that's when he clicks on a button it doesn't work this is just a UI library it allows you to do it it shows some Little Toaster that gets on the bottom so this is when you haven't thought of writing spudgy's you can just say the action didn't work because you don't have any network and that's way your client when she tries out the feature won't be faced because you've seen that it's normal days not working is the network or whatever once you've done that you can start taking risks and the main strategy today for writing and offline apps is to be optimistic and this is what Twitter is doing this is what I've done in this small tab that we are using for basically monitor Trello boards and get notification when developers do some stupid things but basically as you can see Arnold playing mode I can just subscribe on subscribe to board the UI just says it's going fine I don't need to know about the server not getting the data because I assume that the data is going to be sent to the server when my connection is restored which is great because that way the responsiveness of the app is great and the guy can just do whatever he wants in the subway when it gets out the the requests will actually be sent to the backend and so that's that's pretty nice and so the idea concept behind that is you introduced we saw before the three classic actions in Redux request success failure in that case you have something called the comet action and a rollback action and the idea is that comets you're going to get that action when the server actually responded correctly to your request once your connectivity is restored and it's going to be a rollback when the server actually answered with a failure which means that you then need to take that into account in your reducer to update for instance if I wanted to subscribe and it turns you on subscribe when I had no network but then when I got out the server somehow failed I need the unsubscribe to go back to subscribe because my action actually didn't work so you need to be able to roll back when you've been too optimistic as to what should happen so the simple implementation of how you could do that the idea is to store in your redox store some sort of a queue so those are different steps in time and let's say for instance that you do a first request let's call a black request that's a redox action then you have a blue request that you do right after and because you have no connectivity they're just going to stack in this queue that you hold in your store and when connectivity is restored the queue is going to be emptied once at a time first in first out so for instance in that case the black request failed so maybe you want in your reducer to a big GUI to reflect that there's been a failure but it did get out of the queue because the server the request was sent to a server and then the blue request actually succeeded so in most cases you won't want to do anything in your register when it's exceeded because you've been optimistic but maybe you actually need some extra data from the server to update further your UI so you may want to also listen to this commit action type so what's what's amazing is that something has been made to simplify different strategies skál redux offline this great guy did it the idea is that it handles all your read issues that I mentioned before by wrapping reacts persists and all its options so you can just use it instead of redux persist and it handles your write issues by implementing this optimistic strategy for managing your write actions with this queuing mechanism so it just wraps all the net info stuff to to implement this thread it's a quite recent so it's a bit unstable at the moment but I found it quite easy to integrate it it's basically a reduction answer that you can add in your your app and it does two persistence persistence by default this is the basic configuration of it in that case you you want to have an action that is optimistic to follow a user so in that case you see that the effect is what you actually want to happen when the connectivity is restored by default is going to do a fetch request to this URL to the post request with somebody that you got in your interaction and you then have two other fields which I mentioned before the comment field which is going to trigger this action with some metadata as you want and the rollback one that is going to also be sent when the request fails and then in that case you don't want to do anything for commit you just want in rollback to remove what you've actually hoped it would happen when you were optimistic so this is the the very simple example how you can do that to have optimistic updates and get think back when after the network a couple of things you can customize actually once you want to implement Redux offline you can change how Network requests are made maybe you have some sort of the middleware to handle your API that can be done it doesn't have to be using fetch by default you can also update the strategy for detecting the network maybe you want to just hit or whatever to check if the network is available because you don't want to use net net info for some reason you can also change the detection of records calm pronounce that word errors and the idea is that if the server answered with something that shouldn't be retried it's it's that kind of error and you want them to skip the retry strategy that reacts offline does by default when you have for instance and week network in the server Convy reach it's going to have some retry strategy that you can also modify by default is going to retry I think in a couple of seconds then the double of that then the double up until ten minutes I think until your request actually gets to the server which is quite practical and you can you can customize all that to to match your needs also works with react web it's it's using not netting so to detect the network but it works in a similar fashion so once you've used this great library you as in reading you also want to warn users that you're taking a risk and you're being optimistic about things this is a simple implementation of that as you can see I'm on offline mode I'm on play mode I'm unsubscribing and I want to maybe display some sort of errors saying that the device is currently offline this is what Facebook was doing actually but I can still make changes because we trust that once my connection is restored the changes are going to go to the server this is like a very simple strategy that I implemented here you want yeah I want to make the warning appear when I first make a request like you want to click on subscribe I want to make that a happier I wanted to disappear on its own after a certain period of time because I don't to be like blocking the whole screen or if connection is restored that's why I just want to hide it and finally I want to prevent the next warning with from appearing using some sort of a delay to not have that's a bit like I have on the gif you want to wait a bit before seeing it again otherwise it's quite annoying for users and so this is where a side effects a comment coming to come in quite handy for for some of you who know Redux like it-it's going to be easier those who don't might be a bit higher the idea is Redux a gap basically allows you to handle side effects in your app in that case it can well be well described with side effects this is what I'm doing I'm watching actions that require the network in that case I wanted to watch both subscribe and unsubscribe actions so this is with Redux a guy can just yield take which will be basically ID is that I will go to this line only one one of these actions have been received so if I get a subscribe I'm going to the code is going to go there and if I get an unsubscribe the code is also going to go there and it's all in a while loop so you know that is going to be running in your whole app lifecycle then what I want to do if I receive one of these action is to handle another January function that is going to do this whole action dispatching side effects and then I want to wait before going in the beginning of the world true because I don't want this to appear all the time so for instance here I'm just calling delay with 6000 and this is not going to go back there until 6 seconds so this allows me not to have this show up at every time now what I do in the whatever I wants to do when I receive one of these actions I want to first get in my states the online bullying that Redux offline gave me for free it's always in my store once I have the online status if I'm actually offline I want to just send an action that would be captured in the reducer to update this UI for instance let's say that shoe connection alert is going to change something in my state that allows me to show this toaster and then I want to do some fancy race condition it's what I said before I want to either go to the next line if four seconds have been spent or if my connection has been restored and I can do that by listening to redux offline action statutes changed if the pillow is saying that the app is now online so again I'll give you my slideshow check it out but basically once the race condition is finished I can just hide the connection alert so I'm describing insta gasp basically what I've described before then in your UI you can simply have a screen I'm using the drop down alert I presented before if I use not to have the connection alert show and now I wanted to show I'm going to just show the drop down using the library I present it offline device for instance and if on the opposite I'm switching from a state where I did want to show the alert from to a state where I want to hide it I just dismiss the drop down so this is out and very simply put that on the bottom of my views and it's going to work enough not a dated that it's supposed to be view sorry I'm planning to to to provide these small sagas generators so in react react stager offline to provide more help to more sugar syntax and handling side effects above redux offline so follow me on Twitter and hopefully it will be out quite soon and so the last step that you want to take care about is if you're being optimistic there might be conflicts and so this is something that is not handled probably by redux offline let's for instance state two guys are making write requests on a shared resource and they're both in the subway and the result is suppose to be contradictory so there's a conflict there it's a bit like you're doing some weird get stuff with your another developer you want to handle this and in that case you're going to need to do some things in the backend because this is conflict resolution in the backend you you're going to need one these guys so this is most people put in Patrick West and what can you do when you want to handle conflicts so this is a very simple use case where you have this person this first object is being fetched by three different people in time this is time okay and then these three guys wants to make an update at certain points in time but they're offline and those are the times where the actual requests lend on the server because guy number one just got up is away yeah number three guy number two and so you have lots of different strategies as what how you can handle this the default strategy of course is that guy number two is going to win because this is the latest request that gets on the server so if you do nothing this is what should happen but then you might want to say that guy number two should not win because he didn't actually he made an update on something that is outdated because guy number one got there first he made the change so another strategy might be to say the guy who wins is actually the first one in the line because he's the one who made a request of change on some fresh data and all these guys request a change in something that is outdated and it's not that any more on the server the last strategy that you could have is very simply to say that the first guy who made the request while he was being offline should win and in that case it should be guy number three two wins so if you want to there are some use cases where it might make sense for you to actually handle these other strategies but the only strategy that doesn't require you to trust timestamps sent by the client device is obviously the default strategy where you don't have any knowledge of when data was fetched by the client because this is a time sent that we put once the data stretch or when the request was actually attempted when he was offline if you however want to trust the client because it doesn't matter in that use case you can just store this is created ads which are an updated at which are quite classical and databases but you can also store a Colin holding the updates attempt to that which matches these timestamps in that case you're going to be able to handle all the strategies I a few of these helpless because I had this problem I don't know if one of you guys knows feathers J s which is a cool node framework but I provided a couple of these strategies and the idea is that you want to reject with HTTP 409 error if there's been a conflict which is the standard verb for it if the update is rejected this is quite quite simple this is a hook that would be on your phasers code what you want to do is basically grabbed from the payloads the updates attempted at time stamps that is provided by the client because you trust the client and if for some reason the timestamp that you got from the client is actually after the time step that is stored on the server currently storing the server then you want to inject with there is an earlier modify attempt which means