AWS Amplify DataStore with SwiftUI 2.0 | Todo App

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's going on everybody it's yeah boy kela loco and today we are going to be doing something very interesting we're going to be creating to-do app from scratch and we're actually going to be using Swift 2.0 to build out the UI and we're gonna be using AWS amplified data store in order to store all of our to dues to the device for local persistence now I work at AWS and I'm actually working with data store quite a bit and I gotta say I'm super excited about it and it's really easy to actually implement into your app and to just save things delete things and even sync it with the cloud we won't be doing the syncing today but when I say that it's super easy I mean dang it's super easy so that's what we're gonna be using today data store we're gonna be creating we're gonna be reading and we're gonna be deleting so let's go ahead jump right in alright first I want to just start by saying that you can go to aws.amazon.com slash amplify to get to this page and you can actually go through the documentation they have actually made it very straightforward and since I'm on this team it's my job to make sure that everything works as easy as possible for you so if you do find any problems make sure you reach out to me just go ahead and make sure you get go to the getting started you can start a tutorial very easy to get up and running one thing that you will need to do is you need to install nodejs and you need to also have obviously you're going to need cocoapods and you will need the amplify CLI now nodejs you can download by just clicking right here and you can open up that tab and just go ahead and select 12 point 18 point 1 which is recommended for most users you could go ahead and download that then what you're going to want to do is you're going to want to run npm install right here in your terminal so just go ahead and open up your terminal copy this paste it in hit enter and you'll be good to go from there you'll be able to start following along with the rest of the tutorial so let's get into it alright let's go ahead and start by opening up a new project there we go we're gonna do single view application and I'm going to actually be using the swift UI interface and life cycle as Swift UI go ahead and call your app whatever you would like just make sure that there's no spaces in the name alright now that I have my project named let's go ahead and create it alright perfect so now that we have our project all opened up you'll notice that I am actually using the app main to initialize our app and we're going to be opening up the content view like so so let's start getting some of the UI put together before we actually add amplify into our project which is going to allow us to store the data to the device first things first I want to create a list of items so what I'm gonna do is I'm gonna create an array of to do's but since we actually don't have a to-do object yet what I'll do is I'll just use strings instead alright great so now that we have this array of to do's and as you can see it's just an array of strings just as a placeholder for the time beam so that we can see our UI in action next what I want to do is I want to actually add this to dues array into a list so let's add that now alright so we created a list and we used for each to loop through all of our - dues right now since it's a string we're going to use the ID as self as long as it's a unique ID or a unique string then that should be fine and then we're going to have the to-do sent into a text so that we're able to actually render it to the screen so let's go ahead and resume our preview on our canvas now alright great so as you can see we have a lot of uu IDs already listed out right here next what I want to do is I just want to make sure that we wrap this in a navigation view so that we have a nice big sexy header at the top so let's do that now look in really schnazzy it's coming together really quickly really nicely now what I want to do is I want to have a little button here at the bottom where I could tap in order to actually bring up my new sheet which we're going to be using in order to create a brand new to do so let's go ahead and put our list inside of a V stack so that it can be behind the button that we're gonna have down here and we'll also add the button inside of that V stack alright so as you can see we added our list inside of our V stack and now we have our button inside of our V stack and the buttons down here it's kind of small looking kind of weird I think that we should add a little bit of sexy style into it so I'm gonna do that right now alright looking good looking good now I actually realized that I didn't want a V stack but instead actually a Z stack so let's change that up alright perfect so now we actually have our button over our list and it's right in the middle right where I want it to be except not so let's wrap this in a V stack and we'll push it to the bottom and what I'm gonna do is I'm gonna add a spacer at the top to push it all the way to the bottom but also I don't want it to go all the way to the bottom so let's add a small little spacer with a small frame that will keep it from touching the very bottom as well this way I could sit somewhere right here where my thumb would be alright perfect so now we don't have like something ridiculous like a button sitting right in the middle of the screen it's down here where all the actions should be so that's looking really good so now what we want to do is we actually want to go ahead and make sure that this is all working so let's go ahead and run it on our simulator ooh that's quite exquisite if I might say so myself all right perfect so what we want to do is we also want to be able to delete right so we need to be able to swipe to delete so let's go ahead and have that functionality in real quick I'm gonna add it to the for each so that we're gonna be able to do swipe to delete on each to do and we're also going to add an action down here or a function towards the end of our content view in order to call delete properly so let's do that all right so as we can see I created this function called delete to do at index set and all we're gonna do for right now is we're simply going to print out which index were deleting now up here under the for each you can see that I actually added the on delete and we're calling perform delete to do now we're able to do it this way because it has the same function signature as what perform is expecting so we can just call delete to do just the name like this and that will work out perfectly so let's make sure that our delete functionality is in place it won't be taking away from the to do that we actually tried to delete so it will reappear but as long as we get that functionality we should be set all right I'm going to delete the second one looking good looking good it pops back up as expected and we also get the print statement at index one so that's working perfectly fine all right now what we want to do is create a new to-do view so let's create a new file for that swift UI view all right so now that we have our new to do view let's go ahead and start laying it out what I'm gonna have at the top of this view is going to be a large title that just simply says enter a new to do and then I want to add a new text editor which is something that was just recently added in iOS 14 and that's going to essentially control how our user inputs they're new to do value then I want it to be followed by a button down here at the bottom and we're going to also once again add a little spacer towards the bottom just so that it's somewhere right here not all the way at the bottom but we want to have a button down there that's going to allow us to save the to do so let's start adding some of that stuff in right now all right so we have everything laid out and let's go over what we have so far we have the V stack it's going to hold all of our elements in our view and this is going to be our label at the top that's just gonna say enter that new to do we have our text editor we have our button which is going to be the Save button and then we have our spacer just keeping it from having the button all the way at the very bottom of the screen now this looks alright but in my opinion it's pretty damn ugly so let's go ahead and add a couple of modifiers make it sexy again all right perfect look at that way better looks way better in my opinion so now everything's all formatted and laid out the way that we want it the only thing that we're missing is we need to have some text right here because right now we just have this placeholder so let's go ahead and add some text which our text editor is going to be updating every time the user taps something on their keyboard look in pretty snazzy if I might say so myself so that should work and then lastly what we need to do is we need to add a four-hour button so let's go ahead and add an action that's going to simply print out the to do and that's going to be essentially saving it when we get to it later so let's add that in now alright let's go ahead and run this and see what happens alright so we still have our list we have our little button but when we tap on it nothing happens so in order to get this new to-do view to show what we need to do is we need to go back over to our content view and add in a state value that's going to allow us to toggle when we're supposed to show this to do so let's add that in right now alright so as you can see we have our show new to do and what we're gonna do is we're gonna add it into this button action right here and we just want to toggle it so it's going to be set to false every time we tap on it it's going to be set to true it's going to toggle it and then from here all we need to do is we just need to make sure that our z-stack has a sheet on it so that it knows when to present the new to do so let's add that now alright perfect so now we have our sheet and it's binded to the show new to do property and all its going to do is simply show this new to do whenever it's toggle to be true so let's go ahead and run that now and see if it works all right Liz still works tab button shows the new to do looking really good and when we hit save it doesn't print out anything until we actually enter something in alright and then we hit save and it prints out just as we expect it to so so far so good we have pretty much all of our different operations working I think the only thing that we should probably do is just make sure that whenever we hit save it does dismiss this view so let's go ahead and add that in real quick going back over to new to do view and are saved to do what we want to do is we want to access a new property that we have to add that's going to essentially handle when this should dismiss so let's go back up to the top of our new to-do view and add in an environment property that's going to be called presentation mode all right we have our environment variable right here presentation mode and all we need to do is dismiss our presentation mode like so let's go ahead and run that one more time and we hit our button we have our entering new to do we need to do and then we hit save so it dismisses that sheet and then it also prints out the new to do so we are all set to go and add in AWS amplify data store all right so let's head back over to the docs and as you can see it's just letting us know to set up a new project and what we're gonna actually do is we're going to install this using cocoapods so let's go ahead and bust out that terminal and let's change directory into our projects directory alright so as you can see we're in our apps directory if I do LS I can actually see which directories are currently in there what we want to do is we want to do pot in it so that we can have a new pod file let's go ahead and open up that pod file with whatever editor that you feel comfortable with in this case I'll just simply open it up with Xcode now that we have our pod file open there's a couple of different things that we want to do what we want to do is we want to first uncomment out this platform stating that we're going to be using the iOS platform and then we also want to make sure that we're targeting something that's 11 or above in this case I'll go ahead and type in 14 since that's what my current app is targeting you just need to make sure that it's 11 or above or else amplify will not work and then over here what we want to do is we want to add in the different dependencies that they have listed right here now we won't be actually deploying to the cloud so we won't need this pod right here but we will need these other three pots so let's go ahead and copy these and add them into our pot file alright great so now we have amplify we have amplify slash tools and then we have amplified plugins and the plug-in that we're using today is going to be the AWS data store plug-in the library that's responsible for storing our data to the phone locally let's go ahead and make sure that we command s to save that and head back over to our terminal and now we just simply run pod install repo update ooh baby I love it all right so next what we want to do is we want to make sure that we close out our Xcode project we just want to close it so that we can open up our workspace I'm gonna open up the finder so that we can make sure that we open up the workspace so let's do that now with open period all right great so now what we have is we have our workspace so we can go ahead and open this up alright now that our workspace is all opened up let's go ahead and import amplify and press run and make sure that everything works as expected all right we have amplify imported let's run it see if everything works all right looks like everything's working so far so good perfect so now let's go back over to our documentation and let's see what they're expecting us to do next now the next step over here you're gonna actually see that we're going to add a new build phase and what this is going to allow us to do is it's going to allow us to run the script that is provided by amplify in the background the amplify CLI and what we're going to do is we're gonna add a new build phase to our project so that we don't have to actually go into the terminal and do anything manually so let's go back to our project select the project we're gonna go to build phases and we're going to hit the little plus so that we can create a new run script phase from here what we want to do is we want to make sure that this is dragged above compile sources and we're gonna expand it once it's expanded we just need to go back over to our documentation and we just copy this this path right here into our build phase so let's go ahead and replace all this with our path and as you can see all it's doing is its getting the location for the pods root and then it's going to use amplified tools and it's going to run a script for us in the background and what's really cool about this is that if we go ahead and look at our project navigator we'll notice that our folders we only have what four different folders but after we run this it's going to build up some folders for us and configure amplify in the background and we don't really even have to do anything other than hit command B to build all right now that it's finished building as you can see there's this new amplify config folder and it comes with four different files in it the two most important ones for this tutorial are the schema graph QL this is essentially how we construct an object since amplify uses graph QL in the background we construct a graph QL schema in order to create our objects and all of these types are transferred into swift's types and then the other one is the amplified tools now the the thing that we're going to be focusing on here is the model gen and this is essentially saying whether we want our models to be generated for us or not we're going to create a model and then we will go back and we'll turn on model gen so let's go ahead and create our to do type all right perfect so as you can see we create a type this is essentially saying we want to create a new object and then we give it a name so in this case it's called to do and we're using app model which is essentially telling amplify that we want to create a brand new table that's going to store all of our to dues now what we want to make sure is that we have an ID and that the ID is mandatory now usually when you see the exclamation points and Swift it means force on rap but in in our model all that means is that we're going to have a mandatory field so it's it's just saying that it's not going to be an optional field so ID cannot be optional and body cannot be optional and this is just a type string so ID will actually be translated to UUID and string will just be translate to string if we were to ditch the exclamation point at the end of this body we would actually notice that body would be an optional property on our to do object but that's not what we want here so let's go ahead and make sure that it's in there next now that we have our art to do model setup let's go over to the amplify tools XC config we're gonna turn model gen to true and once again that just tells amplify that we want to build out our model which was to do so let's go ahead and hit command b to build again all right perfect it looks like a finished building and what you'll notice is that it's going to hold art to do object which is generated for us and keep in mind that each of these files are generated so we don't want to actually put anything else in here so if you ever have to add anything else we're gonna do that in a separate file and an extension but we have our to do object right here which was generated for us as you can see it's a string and a string and we're just going to simply initialize our UUID with a UUID string and our schema is all generated for us right here as well so what it does is it allows us to have coding keys all kinds of different stuff that allows it to be stored into datastore for us and we don't even have to write any code for that and then the amplifi models is simple simply registering the different types of schemes that we have in this case it's just to do so let's go ahead and go over to our app area which is the swift UI to do app and in the struck that's marked with main and conforms to app what we want to do is we actually want to add a function that's going to be called during initialization that's going to allow us to configure amplify so first what we need to do is we need to import amplify and amplify plugins and then what we'll do is we'll add the configure function which will be called in our an it method all right so as you can see we have amplify imported we have amplified plugins imported we have our an it method right here and we're just going to simply call configure amplify which is our new function down here next what we need to do is we need to add datastore as a plug-in into amplify and run configure and we'll be all ready and set to go all right so as you can see we're using amplified add to add in our AWS datastore plug-in and the AWS datastore plug-in needs to be initialized with some models so remember that our amplified models was generated for us during on our last build and what we're going to do is we're just going to initialize an instance of that and pass that into our datastore models from there we're going to run amplify configure and if all that works out then we'll see amplify initialized but if not then we'll get this error printed out now before we actually run build again I just want to make sure that we go back over to our amplify tools and make sure that model gin is set to false it wouldn't be a problem if it was still set to true but it will take a little bit longer in order for everything to finish building so let's go ahead and run that now all right perfect I love when everything starts coming together so let's go ahead and head back over to our content view because we're pretty much all done with our setup we have our configs for amplify we have the models from amplify and now we just need to head back over to our content view and start updating where necessary so first thing I want to do is I want to change this to dews from an array of strings to an actual array of - duze so let's do that now alright with that change we noticed that we do have some code that does begin to break one of which being for each is not going to work like this anymore in order for - duze to be able to be passed into for each as an argument and not have to specify the ID itself what we can do is we can add an extension on our to do we can make it conform to identifiable so let's go ahead and add a new file now that does that so this will just be a swift file all right and let's just make to do conform to identifiable very simple and straightforward nothing to do here since to do already has an ID field on it that was automatically generated for us the same ID that's being stored for the table under the hood perfect let's head back over to our content view and now we can simply remove this ID field right here and also what we want to do is we want to make sure that it's not gonna try to pass in to do to a text but actually the body of the to do so let's add that in looking good looking good and I think that's all the breaking changes that we have so now let's go ahead and run this again make sure that everything's working and we should actually see a blank list all right there's that blank list I told you that we would get a blank list I told you all right so there's a couple of things that we want to do in the content view we want to be able to get the two do's when the screen first launches right we want to get those two do's and then we also want to be able to observe any new to do's so let's go ahead and start by adding in the get to do is first and then we can add observe to do a little bit later so I'm gonna put it right above the delete to do and we're just gonna create a new function called get to do alright so as you can see we are accessing amplify since we have it imported at the top from earlier and what we're going to do is we're gonna access the property called data store and data store has a property or a method on it called query so then what we need to do is we need to pass in what we're querying for what type of model we're gonna be querying for in our case it's going to be to do and then we have this completion handler which will either be successful or will fail if it's successful we'll get an array of to Do's if not then we'll get this error so let's go ahead and implement that now all right and done it's that simple simply amplify datastore query and then we say we want our to do we get a result did we get it back yes then we have to do is no we got an error and then right here all we need to do is just do something with our to Do's in this case we're printing them out but let's also send those to dues to our to dues in our state variable perfect let's go ahead and run that and make sure that everything's working as expected if it does work as expected then we should be left with the empty array but if not then we should be able to see what's the problem and why we're getting an error and probably one of the most important things that we do is we need to make sure that it's actually called so let's go ahead and add that to the bottom of our navigation view this navigation view at the bottom what we're gonna do is on up here we'll go ahead and call get to dues there we go let's run it again alright and as you can see we get our empty array which is perfect that's exactly what we expect it to get so now we're able to get the two dues what we also want to do is we want to be able to observe when we do get new to dues so this is going to get the two dues when we first load the screen right whenever this screen appears but we also want to observe whenever there are changes on the to dues so this will allow us to observe one there's a new one created or if we end up deleting one then we'll be able to handle each of those events so let's go ahead and create a new function called observe two dues all right so as you can see amplify is really pushing the envelope and using the latest and greatest technology right inside of their libraries and we're actually going to be using publisher which is provided to us by combine and what we're going to be able to do is we're going to be able to use this publisher just like any other publisher so what we'll end up actually doing is will sync this publisher and then handle the values that come in but before we do that let's make sure that we're importing combined all right so now that combines at it let's go back down over to our observe to dues method and let's go ahead and add in that sync method alright so we have two closures that need to be passed into this sync method so the first of which is going to be handling if there's an error or if there's a completion on the actual publisher itself so that means that this would no longer be sending the events down the timeline and we're going to be handling that in the received completion however what we are going to be mainly focusing on is whenever we receive some changes whenever there's a mutation event in our table saying oh there was a new object in this case but to do object created updated or deleted now we're only going to be focusing on creation and deletion so those are the only two mutation types that will be handling but if you wanted to observe updates you could also do that as well so let's go ahead and add in some values for these closures and we'll go back over what's added alright so as you can see we filled in our sync method for the receive completion all we're gonna do is we're gonna take the completion that's passed into this closure and we're going to check if it's a failure and that's the only time that we're really gonna care about it for the most part is if there's some type of error then we would want to probably do some error handling if it's finished then there's probably not a whole lot that we would be doing but if there is an error we're just going to print it out now for the received value once again we're receiving some changes that are being passed in and we're gonna switch on the mutation type now at this moment the mutation type is of type string I had brought it up and it may be changed in the future but right now we're going to be working with strings and one of which is going to be create and the other is going to be delete and we're going to add in our code for creation and deletion what we want to do when something is created what we want to do when some things delete it if it's anything other than that we don't really care and we're we're not actually going to do anything so one other thing that we need to do and what's also provided by these changes is it allows us to decode the model that we're expecting so what we can do is we can actually get the object and decode it to whatever model we think it's going to be in this case it should be a to do object we can decode that and we can simply if we're creating we can add it to our to Doozer array if we're deleting then we can remove it from our to Doozer right so let's add that functionality in now all right so as you can see like I said we're going to simply decode this model as it to do and we're gonna just optionally try to do that and then as long as we get that model we're able to grab our to Do's and append it to our to Do's if it's create but if it's delete then we're gonna get the first index of that to do and we're gonna actually remove it from our array now in order to clean this up so that we don't have to do this little syntax where it's ID is equal to ID let's go ahead and add another extension to our to-do which makes our two dues equatable alright so as you can see we added the equatable protocol and in order to conform to that we need to add a function which is essentially the double equals and we need to have left hand side LHS equal to it to do and RHS which is also equal to to do the right hand side and then we're going to be returning a boolean value so as long as the left hand sides ID is equal to the right-hand sides ID and the left hand sides body is equal to the right-hand sides body that means that they are the exact same object and we're just returning true if if either of these values are different than it would essentially be false now let's go back over to our content view and now we could do first index of our to do so that makes it look a whole lot cleaner so we're still getting this warning up here at the top saying that the result of call sink is not being used and essentially what it's saying is that sink returns an any cancelable object essentially saying that this thing needs to be kept in memory in order to continue to observe events if you were to do this or if you were to do this just to get rid of the error what would end up happening is that this would fall out of memory and the observations would not actually persist and you would not actually get any of these events changing so what we need to do is we need to go back up to the top and we need to make sure that we're keeping an instance of art any cancelable all right we have our any cancelable observation token and it's just gonna be optional because we're not gonna set it right away but we will eventually set it once observe to doose's called so let's go ahead and add that in now and as you can see it makes our error go away and we're making sure that we're hanging on to this this subscription so now we just need to make sure that we're calling observed to dues in our on appear alongside our get to dues so let's go ahead and run that make sure that there's no errors popping out anywhere we're not doing anything yet with saving any data so we still won't see anything pop up and we should still see a empty array print it to the console but we're gonna be adding to do very shortly and here it comes oh yeah look at that empty array I told you I told you alright so let's go ahead and go back over to our new to-do view and we're gonna be importing amplifi here too and now what we want to do is we want to go down to that save to do area and this is where we're gonna actually add the save functionality so it's actually extremely easy to save stuff to amplify it's pretty much the same structure as we had in the content view so as you can see with observations same structure amplified data store publisher and five datastore query we're gonna do amplified datastore save so let's go ahead and add that in now alright so as you can see it's pretty simple pretty straightforward we're simply creating it to do passing it in and saving it to datastore and then we get a result did we successfully save it or did we get some error if we did successfully save it then what will happen is you can access the the safe to do if you wanted to do that right here but there's no need for that because we're observing it on the other screen so we actually don't need to do that for our particular application but all we're gonna do is if it's successful we're gonna print saved to do and then if it doesn't work out then we're just gonna print an error so let's go ahead and run this and see if it works all right here we go so no to do's at the moment we hit the plus it brings up our enter in a new to do so I'm going to enter in a new to do all right so this is gonna be my new to do watch all the latest WWDC 20/20 videos I'm gonna save that and as you can see we have the safe to do and it actually detected the the new to do came in before it even saved the to do which is really cool it's really quick and as you can see here we have watch all the latest WWDC 20/20 videos let's go ahead and add one more end all right so our next to do is gonna be called go through the documentation for Swift UI and as you can see it's added in right there so we already have our two do's listed right here and let's make sure that they're being persistent to the device by stopping and running again all right and as you can see they're still there they're still there they safely came back for us all right awesome so lastly what we need to do is we just need to go back over to our content view and make sure that we're handling the deletion of our to do's now in order to look to delete it's pretty much the same structure just as before all we're going to be doing is amplified data store and then we're going to be simply deleting to do but first what we need to do is we need to make sure that we get out that to do that's supposed to be deleted so let's go ahead and do that now alright so as you can see my amazingly code algorithm right here in order to find out the deleted to Do's since it's so hard to figure out which to do was actually delete it this is one less than optimal method in order to find out which to do was deleted so what we're doing is we're going to create a copy of our to dues array and we're gonna store it as updated to dues from there we're gonna remove the item at the offset or the index set from the updated to dues so that it doesn't affect our UI now what I'm gonna do is I'm going to create a set from our updated to dues I'm gonna use the symmetric difference in order to find out okay between this set and this array what are the differences and there should only be one because we're only removing one object from there we just get the first object it should only be an array of one but if not then we're just gonna get the first one and as long as we're able to get that we should be able to move forward with it to do now this isn't gonna work right out the box simply because our to do object is not hashable so in order to fix that we need to once again go to our to-do extensions and make sure that it is hashable alright so as you can see we made to do conform to hashable by implementing the public hash function and all we're going to do is we're simply gonna take the ID and the body and combine them and that's gonna be passed into our hash er so now if we go back over to our content view and we get this to update we should see that everything is working as expected and we get that error to go away so now that we have the to-do that's supposed to be deleted we can go ahead and delete that from datastore all right so as you can see once again same format amplify keeps it really consistent and very easy to use just super intuitive so work once again datastore delete that to do we have a result if it's successful once again we're observing so we don't need to do anything with the return to do from the success result we'll just go ahead and print out delete it to do but if there is some type of error for whatever reason then we can print out what the error is so let's go ahead and run this and let's see if our to-do app works all right we're still getting back our data let's go ahead and create a new to-do all right so we have our new to-do march for black lives matters we save that looks like everything's still working now let's go ahead and try to delete and if we go ahead and delete this middle one we see that it goes away nice beautiful animation we also get deleted to do down here and then if we go ahead and run the app one more time we should still see that we only have to to do is left all right perfect so we have a perfectly working to-do app all right and that's pretty much it as you saw it's extremely easy to use datastore to save data locally to the device and if we already even take it one step further we could push this data directly to the cloud and have everything syncing in the background now personally I feel like this is the best experience that I've had with saving data to the device and it's a much easier than working with core data or any other third party persistence so that's just me let me know what you thought about using AWS amplify data store I really want to hear your feedback I think that you should really give it a try I think you're gonna like it so with all that said and done I hope that you learned something today I hope you subscribe to the channel now go out there and keep coding passionately later
Info
Channel: Kilo Loco
Views: 6,912
Rating: undefined out of 5
Keywords: swiftui 2.0, swiftui, swiftui todo, swiftui todo app, aws amplify, aws amplify ios, aws amplify datastore, aws amplify data store, amplify data store, amplify datastore, aws datastore, aws data store, aws data store ios, amplify datastore swift, amplify datastore tutorial, amplify datastore example, up and running with aws datastore, datastore swiftui, datastore ios, datastore swift, swiftui texteditor, swiftui present new view, amplify cli ios, amplify cli datastore
Id: n1mV6m4xBF8
Channel Id: undefined
Length: 40min 19sec (2419 seconds)
Published: Wed Jun 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.