Hello and welcome back. Background tasks are crucial for building fast, reliable and well-crafted applications. In today's video, we'll take a look at the new Expo Background Task module and how you can use it to run custom logic even when your app isn't open. Background tasks are super useful for things like syncing user data, downloading over air updates ahead of time, prefetching content, and making sure your app feels instant when users come back. Let's dive in. Let's talk a little bit about some important things that you need to know about background task before we jump into coding. So let's start by checking out the demo. Today we're building a simple daily go to app. Users get a new quote every day and they don't have to wait for it to load. When you open the application, it quietly features quotes in the background using a background task. Save them with async storage and when the user opens the application, we perform a fast check and update the state to show the latest quotes from the local storage. This is going to work the exact same way for Android and iOS, and as always, the source code is going to be available in the description. So when do task run? What are the system constraints best practices in real use cases? Let's dive in. Background task run of course when the app is in background, but also there is a minimum interval of 12 hours, but you can actually set a minimum of 15 minutes to run your background task. So these background tasks are opportunistic and they rely on the network battery, CPU usage and user patterns. So that lead us to some constraints to keep in mind when using background tasks. So if the device has low validity, then the background task are going to be skipped. If the user of course does not have network connection or they have airplane mode activated, tasks are going to be skipped. There's also an option to turn off background off refresh. So as you notice there in this screenshot, I turn off for my application. That means that the background task are going to be off. And finally, if the user quits the application or kills the application by force, quitting it, of course, background task are not going to be around since you depend on the system. Goodwill. Let's talk a little bit about the best practices. It's good to keep task light and efficient, avoid heavy work and batch multiple operations if possible. Some examples of good use cases for background task is sinking user data. So you can use a technology, for example, to solve to sync the database or files or documents or progress of the user. You can do that in the background. Upload logs or telemetry backup data, download over-the-air updates, for example using EA's updates. And we have a code example that you can check out in the documentation of Expo updates. Another good use case is prefetching content for faster user experience. And that's technically what we are doing today with our application. So now let's start coding. I'm going to leave the link to the documentation of background task in the description of the video. But as always guys, you can come here and learn more about each specific platform and use cases. Just a couple things to note is that Expo Background task relies on Expo Task Manager, which is another native module that provides an API that allows you to manage long running task, in particular those tasks that can run while the app is in the background. And as you notice here, this library is actually used for multiple Expo modules like location, background, task, background, fetch, and notifications. And just as I note, background fetch is actually getting deprecated and you should be using background desk in your application. Another important thing to note before we jump into coding is that you can only test background task on real devices for iOS. But actually they work nice on an Android emulator. So in today's video, I'm going to be using an iOS real device. So let's dive in. So here we have a project that I just created. It's a brand new Expo app. The first thing that I want to do is install dependencies. So I'm going to run npm Expo installed react native async storage Expo task manager and Expo background task. So let's go ahead and hit enter. And I'm here in the package.json. And we can quickly see that the dependencies have been installed. And just make sure that the Expo background task is inside the plugins. We are good to actually start creating a new development build that we can solve on a real device. So I'm going to be using ease of course. I'm going to run EAS init, then select my account. I'm going to create a new project. Awesome. Now let's configure my profile. So I'm going to say here is build configure. Hit enter. So this is going to work like I said before for iOS and Android the exact same way. So I'm just going to configure now for iOS. This is going to generate my SJ Json here. And I need to create a development both for internal distribution. And one thing that I want to do, because I want this to be fast, is to enable a large resource. So on their resource class you can choose large. And then I'm going to say iOS build platform iOS passing the profile development hit enter. We need to install the Expo dev client. So I'm going to say yes this is another dependency. And we can double confirm that in here. So it added it by default for me. I'm going to choose the default bundle identifier. And this under encryption I'm going to say yes. Let's log into my Apple Developer account, select my team reader lies my certificates. And just make sure that my device is listed here. Today I'm going to be using the iPhone 13 Pro which is going to hit enter. All right. And the build is in progress. Okay. So in the meantime I think we're ready to start creating some utility functions that we're going to use to register or background task. And also the logic that we want to run on the background task that we're going to be generating. So let's go ahead and create a new file file called utils dots. Let's start by importing background task task manager and async storage. I'm going to define some constants that I'm going to be using later, like the background task identifier. This is just a string determining how my task is going to be named. In this case I'm going to name mine Fetch Task. Then let's set the minimum interval to be 15 minutes. This is good for testing, but actually I wouldn't recommend that you try to run your background task every 15 minutes. Well, I guess depends on what you are doing, but just keep in mind that it's not guaranteed that the background task is going to run every 15 minutes, right? So I would say it's better to assume that at least your background task is going to run once when the user is sleeping and the device is going to, you know, find a good spot for running all the, background task of the device. Then I'm also defining a quotes history key. This is a key for the async storage. So I'm going to be storing an array of quotes that I'm fetching in the background. And this is actually optional that like the max history of items that that I want to store. So I'm going to be setting this to ten quotes maximum. The next thing that I'll do is just create a type for the data that I'm storing. Since we are using TypeScript, of course, and I'm going to be using an API that, you know, returns a quote that looks like this when it was created, I think, and the hour and the timestamp. And then I'm just creating this type quote history, which is a list of quotes. And by the way, my build is ready. So let's go ahead and start this server. Okay. And let me scan this QR code to download the build. Let's install it. Now I can, scan the other QR code for the server. This is going to open the application first time. It's going to ask me to allow access to the network. So let's allow that and then reload the bundle. Awesome. And just like that we have our app, running on a real device ready to start developing. Okay. So let's let's minimize the terminal. So next I want to create a function called initialize back on desk. This function is going to register the task. And also run the logic that that I want to run for this specific task. So this is an arrow async function. And this function is going to expect an inner app mounted promise. This is a promise to let know this function once the the JavaScript of the application is loaded, so that we can run JavaScript code. And we are going to pass this promise from our component. So when the component bounced, you can you can think of it like using a use effect. We can call this inner mounted promise. And then we can let know this initial background task that the JavaScript is ready to run logic. So let's start by calling the task manager and define a task. The name of my task is going to be Background Task Identifier, and this is going to take an async function, which is a function that you know contains the logic of this task. So first of all, let's add a console.log. Here I'm going to say background task start. So in my case I want to do a couple things here like fetch a new quote, save it to local storage and I think that's it. So I'm going to just add another console.log here, just so that we know that the task is done if we hit this point now. But something important here is that since I'm going to be using, async storage to store my new quotes, I need to make sure that the JavaScript of my application is loaded so that we can actually call async storage. And for that, we're going to be awaiting the inner app mounted promise. This means that we are going to delay starting the task until the inner app is mounted. Once that is done, we can actually start fetching the code. So I'm going to have a try catch. And if you want we can have another console.log here. And this one could say something like error fetching quotes. Now it's time to use the API that we want to hit in the background. And I'm going to be using this one right here. And you know, it just returns a simple quote with this structure I'm grabbing a random one using the regular fetch. So let's grab the quotes. This is going to be an array of quotes and we can say await response dot JS. All right. Now we can say if we actually have some quotes and quotes dot length it's greater than zero. We need to save this quote. So let's call await store quote history. And let's pass the quotes at position zero okay. Now let's go ahead and actually implement this function. I'm going to create this function down here. This is going to be an async function. And it's going to take the quote as parameter. Let's create a try catch. If something goes wrong let's just put it in the console for now. So before I save into the local storage I want to first get the current history that I have. So let's get the history dot Json equal to await async storage get item. And let's pass the quotes history key. Let's grab the history if it actually exists. So if this is actually a value we can pass it. Otherwise we return an empty list. And we can type this as quote history okay. So let's grab the new quote from the one that we are getting in the parameters. But I want to add a new field which is a timestamp. And this timestamp is when this quote was fetched. So we can say date dot. Now this is useful for debugging so that we know when the task was triggered. So now let's append these new quote to the history. I'm going to create a new variable updated history, which is an array where I'm going to be including this new quote at the beginning of the array. And then whatever we already had on the history and that's it. Now this is optional, but I'm going to slice this to be from zero to maximum size, which is then since totally optional. If you want you can just keep appending stuff. And it's also worth noting actually that you can check the history and check the last item. And maybe if the last item is from today as well, maybe you want to skip fetching and instead waiting until the last quote fetched was is outdated, right? And then go ahead and fetch again and hit the API. But at this point I want to fetch as many as possible. Okay, now we can store it. I'm going to say async storage set item. Let's parse the key quotes key and the value is going to be updated history. But of course these needs to be a string. So let's call Json. String ify. Finally let's return the updated history and hit save if we come back. Just to recap, we are checking that the response contains at least one quote, and then we store it in the history. And that's all the logic that this, background desk is going to have. So let's go ahead and minimize this task. Define. And after this I want to actually register the task. Since we're going to be calling this function from my component. We are defining the task. But we need to register the task if we haven't already. So I'm going to have an if statement. And I'm going to check if let's use the task manager to check if the task is registered async. We are going to use the background task identifier and this is going to return true or false. So if it is not registered then we want to go ahead and register the task by saying await background task dot register task async. This is going to take the task name. Let's pass the identifier and then it's going to take some options. And the options that it expects is just the minimum I mean it's not expected. It's actually optional. You can skip this if you want, but I'm going to set this to be the minimum interval variable, which is 15 minutes. And you can learn more about it just by hovering. It defaults to once every 12 hours. And the minimum interval is 15 minutes, which is what I'm setting right here. And just keep in mind that on iOS, short intervals are often ignored, and the system typically runs background task during a specific windows, such as overnight, like I said before. So let's go ahead and hit save. So we we're defining the task and registering the task. If it's not registered already. And that's all the logic that this initialize background task function is going to have. Now let's go back to the index of my application just to make sure that we are still connected. Let's say hello. The tasks hit save okay. We get library loading which means this is working. And let's use our initialized background task function okay. Now this is going to take a promise that is going to let's or that is going to help so that we can wait until the promise is resolved to run. Our logic. So let's create these resolver promise. This is going to be a void function or no. So this variable is going to help us store the resolver function. And then I'm going to create a promise. So I'm going to say promise equal to new promise. So this is just creating a promise and storing the resolve function for later. And we are storing these resolve in these variable resolver. Then I'm going to pass the promise here. Notice that this promise is not going to be resolved until we call these resolver. And we can call it using a use effect inside or component. So let's check if we have a resolver. Then we can just call resolver. And we can, you know, put in the console resolver resolver called. And this is resolving the promise to indicate that the inner app has mounted okay. So if you remember inside these initialize background desk when we are, you know, running the logic of the task, we need to make sure that the inner app is loaded. Right. So this is what this is why we are passing this promise. And this is going to await here until the user effect is mounted or the component is mounted. So let's hit save. Now I see on the log that the resolver was called. All right. So now we can start defining some state or our quotes. I'm going to be using Usestate from react. And we can also import our type from the utilities. Let's go ahead and create a new utility function to load the quotes from the async storage. This is going to be a fairly simple function get quote history. It's an async function that just hits the async storage with the quotes history key. And then if we have some data it's going to return it. Otherwise is going to return null okay. So let's hit save. Let's go back. And we can use that function here. Let's create a function down here to load the quote history which is going to call these get quote history function. And then if we actually have some data we are setting the quote history state here. Now let's call this load quote history inside the use effect. Okay. So this is just basically going to load the history the first time the user opens the application. So the next thing that I want to do is count all the way here. And just for debugging I'm going to import all asterisk manager. And we can just put in the console all the registered tasks that we have running. And as you notice my task is actually registered. Let's try to reload the application just to see what happens. And we get this same task again okay. So now that we know that the task is registered, we can actually trigger it manually for debugging. Let's go ahead and do that. I'm going to create a button here. Let's call it triggered task. And on press create an async function here. And then we can use background task. So I'm going to come here and copy this import okay. Let's go down here again and we can say await background task trigger task worker for testing async. Now it is very important to know that this is only going to work for debugging. And basically what this is doing is just calling whatever we have inside here inside of the task that we defined. So this is going to trigger the fetch. So let's see what happens. I'm going to clear my terminal and press trigger task. And as you notice we, started the task and the task is done. Let's try to put the history list on screen. So for that I'm going to paste quickly here some styles. Let's import stylesheet from React Native. And I'm going to render a simple view here. This is just a simple view that is iterating over the history. So if we actually have a history we are going to render in this case the latest quote. And then after that rendering the previous quote. So since I trigger this manually, you can see that this actually worked. We are seeing some data on screen right now. If I go back, or close the app like that and come back, nothing is going to happen, because we actually need to wait. It's actually hard to test, and in my experience, I had to leave my application like 15 minutes there. And you can actually look at the device, like, close the application, lock the device and leave it there for like 15, 20 minutes and the task short run. So if you want, we can trigger the task again. Okay. Desk starter task is done. And then let's close the app and let's come back to the app. Now nothing is happening. Although we know that the background task was triggered. And we also know that we now have two quotes, but we only see one. So a good approach here would be to check when the app is foregrounded and when the app is coming for from the foreground, and when that happens, hit again. The local storage and see if we have new data. And to do that we can actually use the app state. We can import app state from React Native and then whenever using as well a ref from react. And the app state, allows us to subscribe to state of the application. Like if the app is foreground it, we are going to know, and then we can run some logic. So after I'm loading my code history, I'm going to be using this same use effect to create a subscription. So I'm going to call this app state subscription. Let's call app state dot add event listener okay. So we are going to subscribe to change. And when something changes with this event I'm going to run this function. Now this function actually gets the next state as parameter. This is going to be of type app state status. So we're going to import this from React Native as well. Let's hit save okay. And there the application reloaded. So we fetched the previous quote. But yeah let's keep going. So in here we can validate if the app state dot current from the reference. And then we can match these to be inactive or background if this is true. And the next app state it's equal to active. This actually means that the app has come to the foreground. So we can try to call load history again okay. And after these if statement I'm going to set my app state current to be equal to the next app state. Let's hit save okay. So let's try this I'm going to close the app and then come back. And you notice that we get the log apps has gone from the background the foreground. Sorry. And let's do it again. We have the luck and we can have a similar validation to know when the app has gone to the background. So if I add this just to match that the app was active and the next one is background, it means that now we are in the foreground. So if I close the app, I should be able to see that log. Let's see. Okay. So I refresh the application. Now I'm going to close it. App has gone to the background. Then if we come back we get the log as well. And when we come back of course we are calling the load quote history. So that means that if I triggered the task manually. Okay, we did that successfully. Now I'm going to close the application. Let's pretend that the task was actually triggered while the application was closed. And then let's say that is next day I'm coming to my app, open the application, and boom, hitting the async storage, which is super fast, and I get the latest quote at the top. Now, one more thing that I almost forgot is that we need to clean up the subscription when we unmount the component. And that's it for this video, guys. I hope you like it. I hope you learned something new. Background task definitely are a super powerful feature that you can integrate in your application. Check out our docs to learn more about Expo Background Task Task Manager and how you can use it with Expo updates. Don't forget to give it a like, subscribe - and I'll see you in the next one!