Bringing Your Web App to Native With Capacitor

Rate this content
Bookmark

So, you have a killer web app you've built and want to take it from your web browser to the App Store. Sure, there are a lot of options here, but most will require you to maintain separate apps for each platform. You want your codebase to be as close as possible across Web, Android, and iOS. Thankfully, with Capacitor, you can take your existing web app and quickly create native iOS and Android apps for distribution on your favorite App Store!


Contents: This workshop is aimed at beginner developers that have an existing web application, or are interested in mobile development. We will go over:

- What is Capacitor

- How does it compare to other cross-platform solutions

- Using Capacitor to build a native application using your existing web code

- Tidying up our application for distribution on mobile app stores with naming conventions, icons, splash screens and more

111 min
22 May, 2023

Comments

Sign in or register to post your comment.

AI Generated Video Summary

Cross-platform development with Cordova paved the way for a vibrant ecosystem of plugins, but had limitations. React Native provides a core library for building apps, but rewriting an app to work in React Native can be challenging. Flutter may mislead developers into thinking they are creating truly native apps. Capacitor offers a solution that combines the benefits of a native app with access to native APIs while remaining pure web. Capacitor allows you to build a full native app that can access APIs through JavaScript.

QnA

Introduction and Q&A

Short description:

We're going to be talking about bringing a web app to native using Capacitor. My name is Blake Hardington. Let's make sure that you are all aware. There is Q&A, there's chat posts, any questions in the chat or in the Discord channel. There is a Discord room for all of you workshops, so I will be able to answer any questions afterwards and also provide the demo that we used here, so that way you can see the source code from what we built. This will be recorded.

We're going to be talking about bringing a web app to native using Capacitor. My name is Blake Hardington. You can find me basically everywhere online at mhardington. Twitter as mhartington, blue sky as mhartington, Mastodon as mhartington. Once you got a handle, you kind of just hold on to it, and people will follow you around wherever you go. Let's make sure that you are all aware. There is Q&A, there's chat posts, any questions in the chat or in the Discord channel. There is a Discord room for all of you workshops, so I will be able to answer any questions afterwards and also provide the demo that we used here, so that way you can see the source code from what we built. This will be recorded. As attendees, I think you have access to the recording afterwards, so if there's anything that you have missed or think that you missed or you just want to clarify on something, you can always go back and watch it. If you feel like I'm going a little too fast and you're like, oh, I'll catch the replay or I'll just listen along, no harm, no foul, that's totally okay.

Cross-Platform Development and Cordova

Short description:

Cross-platform development aims to reduce the amount of knowledge needed to get up and running, simplify code implementation, and save time by allowing developers to build once and deploy to multiple platforms. Cordova, an early cross-platform solution, acted as a bridge between web apps and native runtimes, but its limitations became apparent as web standards evolved. Despite its shortcomings, Cordova paved the way for a vibrant ecosystem of plugins and additional APIs. However, the process of adding these plugins was often complex and lacked support for modern package management tools like NPM. Building and releasing apps also required navigating a set of intricate scripts.

Now, to get started, we're going to look at what cross-platform actually is. It is a very loaded term that has existed for many years, primarily coming from the video game industry of all places where people would build full libraries and SDKs, so that way they could take one video game that was written particularly for, say, PlayStation, and then they would be able to port that to different run times so we'd have games that are compatible with Xbox, PlayStation, Steam, what have you.

All these tools exist so that way video game developers can focus on building the best possible game ever, not necessarily focusing on how do I get my video game to work on this different hardware? Now, they all came, now the web and native community have adopted cross-platform, but I like to think that they still share some common goals with the video game industry in that the point is to reduce the amount of knowledge needed to get up and running. A web developer shouldn't have to know all these details about a particular environment, they should just know I have these guardrails, everything within here is there to guide me to the best possible path, and then I limit what I need to worry about. I don't need to worry about the underlying infrastructure, I don't need to worry about the underlying run time. I know what I know and I can ship something. They also reduce the amount of code that you need to write. It doesn't mean that the code has been removed, it just means the amount of code that you are responsible for maintaining and writing can be reduced. So while we all can access things like the camera geolocation, Bluetooth, the way that is done can be simplified and abstracted in a way that will work on all those different platforms. And obviously because we are doing things in a cross-platform approach, we are reducing the time it takes for us to actually ship something so that way we are not spending all of our time having to do testing in multiple environments, having to do multiple feature implementations across the different apps and across the different runtimes. This can just be done once and then it is available in all of the other platforms that we want to support.

So I'm going to do some quick history just because I like history. I think it helps show the path to where we are now. But we're going to look at the ecosystem of tools in this cross-platform ecosystem. And we're going to try to compare how they differ and how they got us to where we are today. So probably the original implementation as far back as I could have told... I could tell, is Cordova, if you've heard of that. It's probably phone gap, and you might not have a great... Feeling towards it. That's pretty common. But it was one of the OGs. It basically was the first approach to saying let's have an app that you can run, write once and run anywhere.

Now, the fun story behind Cordova is that it was meant to be a hack and a way to get an app onto the native platforms as a polyfill. Eventually, it ceased to exist where the developers didn't want to actually maintain this project. They didn't actually want to build it to begin with, but because the web at the time didn't have all the features that we know and love today, they had to build this. They had planned to provide some feedback to browser vendors and WebSpec implementers to get these features into the browser so that way they could eventually disappear. But what they did was act as a bridge to the native runtime through the browser by having a little native bridge inside of their app. So you would take a web app, wrap it up in a Cordova runtime. The runtime would go ahead and intercept calls on the, that the user was calling, pass those off to the native runtime, return those calls again, and then serialize it back to the web app. Which is how we got things like this. I see this and I kind of shudder because this is a very old example, but it did provide a good pattern for how plugin authors and module authors should author their code. We have this thing called Navigator, which is a global, and then we have a camera and then we have a Get Picture API. This was a prototype on how to call camera activity, display an overlay, get a photo, and then we could handle passing that data back off to the web app. Only problem is, this doesn't exist anywhere else in the web. So this is all Cordova specific stuff. If we look at what we have now for getting a photo, this didn't translate well. But Cordova had a problem where they couldn't really change any of this, because people were maintaining apps that kind of expected this to work. So they were stuck with it. We have things like camera not existing on a global, this unsuccess, this quality, this destination type, none of these things ever made it back into the web. And so their mission kind of failed as they went along the way. They built out all these plugins and then never really adapted them to the evolving web standards. Now the camera API is but one implementation. There were dozens more provided by the core team. And then this pretty big ecosystem of plugin authors that would add additional APIs that you could just install and add to your project. Now, when you add them to your project, depending on when you did this, there was either download this zip, add these files to this directory and kind of cross your fingers and hope that everything went well. These packages existed in a time before NPM which is kind of hard to believe. Cordova's that old. NPM was, did finally come along and they finally were able to add support for it, but it still did it in a very eh way. A lot of the packages that they were adding were basically we'll use NPM and then we'll still have a complex set of script that make it work with our existing infrastructure because no one wanted to rock the boat more or less on what people were expecting. And then if you actually want to go ahead and build your app and then release it to the stores, there was a set of complex scripts and sometimes on Mac and Linux machines you bunch of batch scripts on Windows, you were getting some batch executions that were getting run.

Cordova and React Native

Short description:

Cordova became a victim of its failure to update. React Native provides a core library that ecosystem has been able to build on top of. Architecturally, React Native and other projects like it are very similar to Cordova. With React Native, you're not necessarily working in pure web or pure Native. Rewriting your app to work in React Native can be challenging and less successful than people try to make it out to be.

So it was very much a complex infrastructure that never was able to update to match what the ecosystem was providing. So, eventually, Cordova kind of became a victim of its failure to update. And it really just was not a good time. If you're doing anything in Cordova now, I highly recommend that you don't. Upgrade the capacitor after this.

Now, what was interesting about Cordova, and some things that I think they had that were pretty interesting, was, again, the web was their main goal. They wanted to just be a giant polyfill to bridge the gap in what the browser was doing. The gap in what the browser could do and what native could do. They really didn't want people to know about a native IDE, which, as somebody who's primarily a web developer, every time I have to open up Android Studio, I kind of get afraid a little bit because Android Studio is very complex. Same thing goes for Xcode. It can be a very complex tool and a very confusing tool, so I think them having the idea of let's go ahead and abstract that away was a good attempt and a good goal, but implementation left a lot to be desired. And then native projects has a DIST target, meaning that your native project should be something that can be configured through tooling and then if, for some reason, you upload it to version control and then your co-worker had to clone it and set it up again, they should be able to get the same steps and get the same features and enablements, like accessing file system in camera, that stuff should be scriptable. How successful they were in that kind of depends, but it was an interesting idea at the time. But it's definitely hindsight's kind of 2020 when you think that this definitely did not work out 100% well, but at the time it was the best that we probably had.

Now at the other end of that spectrum where we have phone gap being one step removed from web development, what's one step removed from doing pure native development? And that's the compile to native, your React natives so to speak, where they have a JavaScript runtime or a runtime in another language. And then they go ahead and abstract the controls, provide you a widget or something that you can use in place of that that isolates the difference between what does text look like on iOS and what does text look like on Android. Now this is something that is a point of... It's a very contentious point where they say it's a truly native app. But there's still a JavaScript runtime in most of these and specifically we'll pick on react native. There is still a JavaScript runtime in there that is executing all of your code. Any of your app logic that you write in JavaScript is going to be executed and needs to be run. So truly native is a... It depends on how you look at it. You might be getting native controls, but native controls are literally not the most important thing. The most important thing is the app code that you are writing to actually fetch requests, to actually go ahead and handle state inside of your app. The UI is just an afterthought, in my opinion. Something that they did do well, which I really think is a good move on them, is having like a standard library around some of these native APIs. So React Native provides a core library that ecosystem has been able to build on top of that you can use to build and expand. But most of those APIs exist and are available without you having to ever install something like Expo.

Now, oh, this slide was a little askew. Now, the thing that I like to make sure that people are aware of is that architecturally, React Native and other projects like it are very similar to Cordova. Where you have your app represented in this blue square, you have this runtime, which is, in React Natives example, vajscore, they have some custom ones as well that go ahead and execute your app and then take calls inside of your JavaScript and then link them with the native modules and pass them off to this bridge that goes ahead and calls out to those features, serializes those results, and pass them back into your JavaScript. This same workflow exists in basically every solution today. There's really no getting around it. This is how Cordova works, this is how React Native works. And as we'll see, they're not alone.

Now, I'm not necessarily someone who does React Native on a day-to-day, so I'm not gonna try to show some code, but I will say, in the demos that I have built with React Native, where it kinda breaks down is having to understand that you're not necessarily working in pure web or pure Native. For instance, if you are in a pure Native environment, you might be used to having direct access to APIs. With React Native, chances are, you're going to be building a Native module, which has its own kind of steps for exposing those low-level APIs to your JavaScript. Or if you're trying to get direct access to it, you're blending React Native into an existing Native app. And then you're in this weird, what app is being running? What processes are going on? It's a very weird place to be involved in. If you're coming from a web app, or already have an existing web app, and want to use something like React Native, you're kind of out of luck right there as well, because even though there are libraries like React Native DOM, React Native web, you're not pulling in your existing web app and loading it in there. You are rewriting your app to make sure that it works in those abstractions, first and foremost, and then hope that you can re-implement what your existing web app does within React Native. How successful that is really depends on engineering power and how many hours are you willing to put up with it. I think it is less successful than people try to make it out to be. There is a lot of work that goes into getting React Native web up and running. And even then, if you're dependent on certain libraries, like one, for example, is called Quill.js, it's one of my favorite, Rich Text Editor plugins, it doesn't exist in a React Native ecosystem. So you're not able to just use that in there and throw that into your React Native app. You're kind of having to re-implement it. So leaves a lot to be desired.

Flutter, Capacitor, and Native Controls

Short description:

Flutter is a rendering engine that may mislead developers into thinking they are creating truly native apps. The controls are rendered on a giant canvas, which can raise accessibility concerns. Achieving AA compliance is difficult, and AAA compliance is nearly impossible. Complex operations can introduce rendering issues and bottlenecks, especially for developers new to the runtime. Capacitor offers a solution that combines the benefits of a native app with access to native APIs while remaining pure web.

And then that truly native thing, it is pretty, it's pretty misleading. So, I'll change who I'm picking on for a moment. And I'll go to something like Flutter, which is something that is very increasing in popularity. Where people think Flutter is giving them a truly native app, they don't quite understand that Flutter itself is basically a rendering engine. You are rendering things to, in this case, a giant canvas. So all your buttons, and all your toolbars, and all your headers, those are not native controls. Those aren't even web controls. Those are giant canvases that are getting rendered on native Android, native iOS, and even the web, which becomes questionable. If you were doing things with accessibility audits, there's a lot of work to go in and make sure that your app is now accessible. Otherwise, at least here in the U.S., you will be sued. AA compliance is really nice. AAA compliance is almost impossible. With these tools, you're not going to get anywhere close to those. All those controls get rendered on the fly. So, if you're doing a lot of complex operations, chances are you could introduce some jank that would slow down the rendering process altogether. I've done it with Flutter, mostly because I don't know what I'm doing, but chances are if you're jumping into a new runtime in a new language, you will run into those bottlenecks, mostly due to lack of experience or this lack of understanding on how this completely new runtime works. Now, you could get around that by just spending more time with it, but it shouldn't have to be an issue. So, this is where we get to Capacitor, which is one of my favorite things. Capacitor, I think, sits at the nice middle point between a pure native app and having access to pure native but still being pure web at the same time.

Capacitor Project and React App

Short description:

Capacitor is a two-part project with a native runtime and a JavaScript library. It allows you to access native APIs through JavaScript calls. The APIs have different implementations for Android and iOS. Capacitor also supports dropping down to the native level if needed. We have a simple React app created using the Veet tooling, and I will provide the links for you to access it.

So, ah, dark mode, we're gonna start to get that one. Capacitor itself is in its, you know, simplest definition is a two-part project. There is a native runtime and a native library that is added to expose native APIs through JavaScript. And then there's a JavaScript library which will publish calls from a web app. So that way the native runtime can hear it and then accept the response. The APIs itself are separated where they have different implementations for the runtime.

So in Android, we're using Android libraries to publish all of our native run times and in iOS, we're using CocoaPods at the moment to make sure that you can have all of those dependencies versioned and managed naturally. There's also a little bit of fun stuff going on with NPM where as you install a plugin, all of the source code gets shipped through NPM and then we're automatically able to link it to your native projects without having to have an extra step involved. And something that I don't have here but is also very true is that you can drop down to the native level if you need to and still have full access to Capacitor's APIs.

And I just have a very simple React app here that is, I created using the Veet tooling. If you wanna follow along, I just ran npm create Veet at latest and then I picked React and I'm using TypeScript mostly because the IntelliSense makes sense for our workshop. If you do not wanna use TypeScript, that's totally up to you, it will still work without it. But I will go ahead. I'm going to init a new project. See, in it to win it. Okay. And then I'm gonna do create, git push work in main. And then browse. And then I'm gonna drop this here in chats. If you would like the link at the start and then I'm also going to drop this. I'll drop this at the end of everything in the Discord if you would like, that way you can have access to it. So I'm going to open up a new terminal real quick just so we can install some dependencies.

What we have is just your standard React project. It's rendering our app. We're in strict mode. We got our components. We can go to the actual. You can see we're just loading up the standard kind of Hello World with some state that we can use here.

Project Initialization and Capacitor Setup

Short description:

If you do not wanna use TypeScript, that's totally up to you, it will still work without it. Let's take a quick look at the project itself. It's a very basic React and Vite demo. We're going to install Capacitor core and Capacitor CLI, which are the core way of interacting with capacitor. Then we'll initialize capacitor. Our app name will be JSNation. Our package ID will be com.example.app. Let's go ahead and let that run.

If you do not wanna use TypeScript, that's totally up to you, it will still work without it. But I will go ahead. I'm going to init a new project. See, in it to win it. Okay. And then I'm gonna do create, git push work in main. And then browse. And then I'm gonna drop this here in chats. If you would like the link at the start and then I'm also going to drop this. I'll drop this at the end of everything in the Discord if you would like, that way you can have access to it.

So I'm going to open up a new terminal real quick just so we can install some dependencies. So actually let's take a quick look at the project itself. It's a very basic React and Vite demo. If you've never used Vite before, it's basically better than everything else, better than Webpack, better than any other custom tooling that I've ever worked with, really, really like it. Highly recommend you jump on that train. But what we have is just your standard React project. It's rendering our app. We're in strict mode. We got our components. We can go to the actual. I'm not, I don't have anything installed. You can see we're just loading up the standard kind of Hello World with some state that we can use here. So let's go ahead and first and foremost, let's just run install to get everything up and running. Another benefit of Vee is that it doesn't have that many dependencies. So if this was create React app and React scripts, the installation would not be eight seconds. But what we're going to do with this is we're going to install C at capacitor core and then at capacitor CLI. So these two dependencies here are going to be our core way of interacting with capacitor. The CLI is going to be our tool that exposes a project-specific CLI. So if you are on one version of capacitor in one project and another version in another project, you don't have to worry about having a global install that could break between different versions, really, really simple solution to a pretty common problem in Cordova. So we're going to let this install real quick. Hopefully, it shouldn't take too long. There we go. And then we're going to initialize capacitor. To do that, we will run mpx cap init. Mpx is basically, if you're unfamiliar, it is MPM's execute this local command, try local first, if not, we will have to go to the registry. But because it's local, we will be fine. So, our app is JS... Yeah, this is JSNation. Here, yeah, JSNation. Not Narshin, JSNation. That's going to be the app name that gets displayed to everybody. Our package ID is going to be something that is specific to our particular project. Generally, this becomes an issue when you have certificates that you're using to sign and publish your app. We're gonna leave this as com.example.app because we'll be fine without it. And then there is a kind of Get Started Next where we can go ahead and we prompt you to create a free account. You don't need to. I would like it if you did, but you don't need to so we'll hit No. And we're just going to go ahead and let that run. So let's check out what the diff is here. Obviously PackageLoc and PackageJSON are different but we also have this CapacitorConfig.ts.

Using Capacitor Core and Adding Plugins

Short description:

Now, at any point in time, if we wanted to make this change before we add our needed projects, let's say for example we wanted to include a space here, we could do this. Capacitor Core provides a lot of common features that you're going to expose. One of them is this Core Capacitor class, which we can use to interact with Core Capacitor APIs. Capacitor itself is available in this project even though we are inside of our web app. There's some nice little utilities in here. Let's go ahead and actually run this quickly on iOS. Let's go ahead and add a plugin here, so that way we can see how that works. Geolocation is an interesting one because there is a browser-based geolocation API, Navigator.Geolocation, which is something that came from Cordova. With the geolocation API, we actually get a really simple way to do location that translates well between these multiple platforms. Geolocation is actually returning a proxy here, which is what we want it to do. Let's go ahead and do Geolocation.RequestPermissions. Request location permissions will throw if system location services are disabled. This is very common with Capacitor. Certain APIs will be available on certain platforms that have that correct model.

Now, at any point in time, if we wanted to make this change before we add our needed projects, let's say for example we wanted to include a space here, we could do this. But this config gets read and is used to go ahead and create kind of seed values for our native projects, which we'll see in a moment, but for now we can just know that it's going to be taking everything from the WebDir of dist, the app name is JS Nation, and then the app ID or the bundle identifier is this com.example.app. Not a big deal.

Now let's go ahead and just run dev. And then we're gonna open up our app.tsx, and then I'm going to open up this in my browser, open up dev tools, and we're gonna do some quick little window management. And we'll zoom in real quick. Now, inside of here, let's go ahead and import from Capacitor Core and take a look at what we got. So Capacitor Core provides a lot of common features that you're going to expose. One of them is this Core Capacitor class, which we can use to interact with Core Capacitor APIs. But the rest of these are a lot more for setting things like plugins, setting different values if you are in the cloud, different values if you are a plugin author. You don't necessarily need to worry about them too much. You're more than likely never going to have to touch them, but this Core Capacitor one is something that we will actually use. So let's go ahead and we'll say, we'll create a UseEffect callback, I'll pass it an empty array, and then we'll just say Console.log Capacitor. Talking and typing at the same exact time, a lot harder than you would think. I am going to move this over real quick and then let's go ahead and save. Now, we have everything that we should be expecting to get. Capacitor itself is available in this project even though we are inside of our web app. If we want to go ahead, we can see some of the things that we have going on in here. We are getting that this is the web platform. Any plugins that are registered with a.web.ts file extension are going to be available and loaded up in here. There's a bunch of other things you can see that if we were in the situation where we needed to check first before we did anything and we wanted to have, a call to FaceID and there's no example of that on your phone. Well, you could do a quick little check saying, hey, capacitor.isNative, callFaceID, or biometricsAuthentication if not, do something else. There's some nice little utilities in here. There is this platform function where we can go ahead and figure out what platform we're on. That's just going to return this platform string. Pretty nice things that we have going on here. Let's go ahead and actually run this quickly on iOS. Actually, now we won't run this on iOS just yet. Let's go ahead and add a plugin here, so that way we can see how that works. I'm going to stop the Dev Server for now just because I don't know how V will handle modifying my node modules. But I'm going to install the ATCapacitor geolocation API. Now geolocation is an interesting one because there is a browser-based geolocation API, Navigator.Geolocation, which is something that came from Cordova. It's one of their very few successes, but it does exist. With the geolocation API, we actually get a really simple way to do location that translates well between these multiple platforms. We'll import from the capacitor geolocation, and we're going to bring in the geolocation API. Now, let's just throw that in our console.log just so that way we can inspect things before we call that API. Now, geolocation is actually returning a proxy here, which is what we want it to do. Because this API is actually acting as an in-between, we don't need it to actually return anything right away. This API is just our, if we were to think of this in terms of classes, this is our abstract class that we are calling. Everything else that we are implementing with on the native platforms ends up extending this abstract class or implementing the abstract class. Let's go ahead and do Geolocation.RequestPermissions. Now let's inspect what this does. Request location permissions will throw if system location services are disabled. We can see this is a promise, that way we know it's going to be a synchronous which is great. Let's go. Cool. Unhandled promise rejection not implemented on the web, but the app is still up and running, everything works as expected. We just know that Location.RequestPermissions is a platform-specific thing. This is very common with Capacitor. Certain APIs will be available on certain platforms that have that correct model.

Geolocation and Platform Differences

Short description:

Most of this is going to be requesting permission on native platforms. The web does not have a head of time request or ask for permission. It is a just-in-time model. We have latitude and longitude available regardless of the platform. Other values like speed, heading, altitude, and altitude accuracy are optional and may or may not be returned by different platforms. Web APIs mostly return longitude, latitude, and accuracy due to privacy concerns. Higher fidelity location services provided by native runtimes may offer additional data. I'll be right back, just need to grab a glass of water.

Anything that requires a permission grant will be available, or explicit requests for permission will only be available on certain platforms. Then we can just throw a rejected promise when that feature is not implemented. I said, most of this is going to be requesting permission on native platforms. The web does not have a head of time request or ask for permission. It is a just-in-time model. We can go ahead and comment that out because we know we're not going to need it.

We're going to say const getLock is going to equal an async function. We'll do error functions today. Where in here, we can say const lock is going to equal await geolocation.getCurrentLocation. I'm just going to make this a little bit bigger easier for me to see. We can see that lock is going to return a position. In fact, we can just de-structure this real quick because I know it's going to also return this coordinates field. Then once we have those coordinates, let's go ahead and say setLock. Then we'll setLat equals towards.Latitude.

Now before we go on, I want to look at some of these things. We have latitude and longitude. Web APIs will return this. Those are things that we have available regardless of the platform. Now the other ones, speed, heading, altitude and altitude accuracy. Notice that they also return null or undefined. Basically these values are optional, meaning that some platforms are going to return them, and some platforms will not. The web will not return any of these. Web only returns a few values. Mostly longitude, latitude and I believe accuracy, which is all the web is really going to ever give, mostly due to privacy concerns. But for higher fidelity location services that are provided by the native run times, that is going to be given to you and you might want to have access to that. You can use things like conditional checks to say, hey, if this is available, render it for whatever reason or use it in your app.

Excuse me, I'm going to, I'll be right back, I'm just going to grab a glass of water as my throat's a little dry, and I shall return. If you have any questions so far, please put them in the chat, and then I can come back and take a look at some of them. Just one moment, please. All right, apologies for that, it was allergies, they are not fun.

Using Geolocation and Native Platforms

Short description:

Let's create another button and call get lock. We'll console.log the coordinates. We don't need to request permission from the browser. We can see the geolocation data. The optional values can be marked as null. We can use nullish-coalescing and optional chaining. We'll change the code to use JSON.stringifyLoc for proper formatting. The location is passed back, and we can render the values in our app. Let's add this and install the iOS native platform.

Just to make sure we're in the right place, we have a card and we can see the count is that. Let's create another button in here and on click, we're just going to call get lock, and then lock is lock. Let's see what we got. Let's see, let is null, long is null and then sometimes when you use TypeScript, you feel like you have to do more work than you really should, but for the most part it should be worth it.

Okay. Why is it still complaining? Ah. That is something I have not seen before. Interesting. Let's just go ahead and instead of rendering that out, let's console.log these cords first. If you happen to know why that's an issue, let's check it out later. Let's just say get lock. We'll clear those random errors and let's just say get lock. Now, here's where we don't need to get permission from the request permission from the browser ahead of time. The browser already has its own thing that is done at the call. We'll say allow this and then you can see we get all that geolocation data back from our app. So we can take a look at what this is, you can see our latitude and longitude, we can see the accuracy, and then all those optional values that could get returned, we're going to go ahead and return null because they don't need to be implemented.

Excuse me, I'm going to, I'll be right back, I'm just going to grab a glass of water as my throat's a little dry, and I shall return. If you have any questions so far, please put them in the chat, and then I can come back and take a look at some of them. Just one moment, please. All right, apologies for that, it was allergies, they are not fun. I am very jealous of anyone that does not have allergies. As I was saying, we can see all the values that are not available at the platform, so as plugin authors, you can actually go ahead and have those be marked as optional. As a consumer of that plugin, we can code around that in a type safe way. For example, we can use some nullish-coalescing. We can use double question marks, or we can do some optional chaining if we want as well. Let's go ahead and try to do this again, and figure out we'll just swap these out then for... Number or null... number or null, does that fix it? Huh. Let's try this. Let's go ahead and just say Ustate, It'll be 0. And then, we'll have another UState. And then that way, we don't have to... Because it was an object. That explains why. All right, so now that I actually know what I'm doing. We'll just change this to be JSON.stringifyLoc. This is just a nice little trick. If you ever want to go ahead and actually format things to look properly, use the other values in JSON.stringifyLoc, use null and 2. That will actually return correct value formats. You can see now our location is getting passed back. We can get those values. We can render them inside of our app, which is very easy once we actually realize that we're doing this using actually JSON.stringifyLoc instead of having to do anything else. Let's just move that out of its own thing. Our lat and long look good. Let's actually add this and do something with native. As I said, we're going to go ahead and stop our dev server and we're going to install a native platform. Much like the geolocation is its own separate package, the native platforms are also their own separate package. So we're going to install at capacitor. We're going to install iOS. This is its own thing.

Capacitor Project and Native IDE

Short description:

It's managed its own dependencies. We can see that Native project has been created. This sync step is how we keep our Native project in our web projects in sync. We can see the workflow for how this should work inside of the output. Let's go ahead and do a quick little build. It's doing a little bit more than just copying the web assets. So it saw that we had capacitor geolocation as a native dependency. And what that did was ran through and searched for all the other dependencies that that plugin could depend on. Let's go ahead and run this open command, which is going to open up our native IDE. You can see we have a quick little structure here. We have this app directory, which is our app project, and we have our minimum deployment targets, category, search analytics, supported orientations, iPad orientations, and status bar style. In the app folder, we have our Capacitor Config now converted to JSON and our app delegate. This Swift code is basically instantiating our native portion of the app.

It's managed its own dependencies. Once we have that installed, we can run npx cap add iOS. Now, in previous versions, we had made it so that way cap add actually went ahead and installed the dependency if it needed to. Having this be something that is done after the fact is just simpler to maintain. Only reason why I bring this up is because people have asked it before. It's easier to maintain it if you install the package yourself. Also, we don't need to guess what package manager you are using.

As yarn was very popular at the time, now we have things like pnpm. Just install it yourself and we'll just find the native binary. We can see that Native project has been created. We get a warning out of here though. This sync step could not be run because we are missing our dist directory. This sync step is how we keep our Native project in our web projects, obviously as name implies, in sync. We can see the work flow for how this should work inside of the output. We can see that we should be building the web code and then we can sync the project to our Native app and then we can start to run this on our simulator or on our Native device.

Let's go ahead and do a quick little build. We'll do build. Let's just fix that real quick because that will annoy me. Cool, and then we can run npm cap sync ios. It's doing a little bit more than just copying the web assets. So it saw that we had capacitor geolocation as a native dependency. And what that did was ran through and searched for all the other dependencies that that plugin could depend on. So as a plugin author, you could depend on something from Firebase. You could depend on something from Revenue Cap. Depend on many third party packages. But we can declare them inside of our pod file, and then know that CocoaPods is going to go ahead and manage including them, versus us having to go ahead and figure out ways to add those to our project. So that's already really, really good. Now let's go ahead and run this open command, which is going to open up our native IDE. Now if you are terrified of Xcode, don't be. It's like iTunes except for native apps. You can see we have a quick little structure here. We have, I don't know if I can make the UI a little bit bigger. I'm not going to try this quick, but let me just see if I could. I cannot. All right, so we're going to have to just hope that you can see this very well. But we have this app directory, which is our, or our app project, which is going to hold all the signing capabilities. I'm just going to do this real quick because it's going to need to, and we're just going to let Xcode manage creating all of these certificates for us versus us having to do it. We have our minimum deployment targets, which is iOS 13. We have our category, so if we wanted to get a little bit ahead and figuring out how do we want to publish this. We have all of our search analytics, our supported orientations, our supported iPad orientations, and then the status bar style. All of these things are basically how our app should appear and behave to the user. These should be set. And then we have this app folder where we have our Capacitor Config now converted to JSON. And then we have this app delegate. Now, let's go ahead. I'm going to make this a little bit larger, and I believe this one does have large modes. That way you can actually read this a little bit easier. But you can see that we have actual Swift code here inside of our app. Now, this Swift code is basically instantiating our native portion of the app. You can see we have some lifecycle hooks here, like application will resign activity.

iOS Project Configuration and Dependencies

Short description:

We discuss the various aspects of the iOS project, including the main storyboard, assets, and Info.plist file. We also explore the permissions API for geolocation and provide instructions on adding the necessary descriptors. Additionally, we cover Cordova backwards compatibility and the use of POD files for managing dependencies in Capacitor projects. We highlight the ease of syncing dependencies using NPM Install and sync, and mention the possibility of adding native dependencies for native developers.

Application did enter the background will enter foreground, become active. And then we have this other one where the application did receive... Let's see. There's one in here when... Notifications, but I believe it had been moved out. We can see when the app had been launched through an app-specific URL. Basically any kind of life hooks that we want to hook into here, we can do so. We have our main storyboard, which is going to show the actual controller and the bridge that we're going to instantiate. You can see how this gets connected to the native app. We have our assets, so splash screen and icons, our view controller, our storyboard controller, so if we wanted to go ahead and have a completely different background for dark mode or if we want to customize how the launch image is actually presented, we could do so. Info.plist is going to be something that we do need to talk about. Now, as I said here, inside of the dev tools, when we tried to call a request permission, we got an error. Basically meaning that we did not have permission or we did not have a permissions API, which is totally to be expected because of the browser, as I said. But the actual native run times do have a really good permissions API. Some of them are pretty obscure. So I'm going to show you a quick little secret. If we come over to the capacitor docs for all the core plugins, you can see the list of all the core plugins that we have and that we support. But we're looking at geolocation, and we can see that we have a set of permissions that we need to add to make our app be able to use geolocation. So let's go ahead, and we're going to add a row, and it's going to be NS location always usage description. Actually, we're just going to use location always usage and see if we can't find that real quick. It should be under the privacy heading, and then we can scroll down and have the always usage description, and then we're going to add one more, and that's going to be privacy location when in usage description. So we have two different descriptors for this app. Basically when the location is asked and requested at the time, or if you want to use the location always. Basically we should never be asked for permission again. There's privacy concerns for both of those, and it's up to you to provide a meaningful reason why people should allow that, but when we are using your location, getting detailed location for demo. Then I'll say, always, well, actually, I mix those up, so we'll just rename them real quick or reset those values. We have two different reasons for why we should be getting location. Either we're always going to want the location or just one time, when in use. Let's go ahead. That's already saved. We'll continue on looking through our project real quick. We do have some Cordova backwards compatibilities. If for some reason you have an existing Cordova app and you want to migrate it, but you still need some APIs, don't worry, we get you covered there. The ones that I did want to look at are these POD files. Now, POD files are basically iOS's version of NPM in our package JSON. You can see we have a couple of different places that we can use some additional dependencies. Here, we have core ones for Capacitor. You can see that we are having the core Capacitor module, and as available inside of node modules, Capacitor slash iOS. Perfectly acceptable way of getting that package, and you can see how we're depending on the POD file and Cocoa pods to be able to retrieve that dependency and it'll automatically link it. We're not having to have an NPM or a link step React Native has to do sometimes. Again, our PackWords compatibility package for Cordova, if you want to, and then our plugins also add this entry once they get installed. If we want to NPM install at Capacitor, what other one do we want to install? Let's take a look at our docs again. Let's just call the App one. We can let this go ahead and run NPNCAPSYNC, it'll go ahead, it'll see that we have a new dependency. Then when we come back over to our Xcode, you can see that we had a little warning like, hey, this file changed, and now we have the new dependency loaded in there. It's very fast, it's very easy to keep these in sync, you run NPM Install and then sync, it'll automatically add these references. Now, these are for plug-ins and for capacitor dependencies, but your project could also have a full set of native dependencies in here as well. For whatever reason, if you want to jump down to that native level and add those dependencies, you could do that and then add them here, and then it'll be just like installing any native dependency. I don't expect many people to be familiar with this as we're mostly web developers. But it's nice to know that if you have native devs on your team or in your department that would like to do this and be involved in this process, they can as well.

Running the App and Getting Location

Short description:

Let's run the app on the iPhone simulator and inspect it using Safari's developer menu. We can get our location and see the additional details provided by the native runtime. This information can be valuable for building delivery apps or mapping services. We can track location changes and make necessary refactors. The geolocation.watchPosition function returns a watcher that executes a callback with new location data. TypeScript provides type information for better development.

Enough talking real quick, let's go ahead and run this. I'm just going to pick the iPhone. Why not, go for it, Pro Max. We're just going to let this build. Behind the scenes, this is running all of Xcode's build processes. Let's get this over to this screen. We'll just set this over here. Now, we see that the app is up and running. Looks great. We have some questionable things going on here, but we will fix that in a moment.

Let's go ahead and take a look at what we got down here. Now, this is a native log file. If you've never dealt with Xcode and native logs, this will give us very verbose information. It's very useful for logging out native projects, but it's not very helpful for us in our web dev world. What we can do though is if we go ahead and go into, in this case, for Safari, I know Safari is probably not the coolest browser, but it is a browser and I love it. We have this option to show developer menu in the menu bar, and that gives us this option to inspect our app running inside of the simulator. Just to prove it, you can see, we can highlight all the DOM nodes, you can see our root, we can see all the stuff that we're rendering out, looks great, very familiar from everything else. We can actually start to see some of the sources here, so we can take a look. This is production code, so, obviously, it's going to be all obfuscated and minimized and all that fun stuff. Let's go ahead and try to get our location. Of course, when we go ahead and we click a link inside the phone, it opens up in an external resource. Let's go ahead and say, get lock. We allow JSNation to get our location. This is going to be our one-time permission, so we can say, allow once, and we can get our location results coming back. Now, we can see the other values that we get back here. Now, you can see that because we are on a native runtime, we get far more details going in here. In fact, if we open up and see where our location values are, it's actually going to do a quick little comparison. You can see that the location and latitude are far different, mostly because simulator will mimic a different location. We can see we get a speed, we get an altitude, we get an accuracy, we get all of this other information that is that could be valuable for our app. Now, if we think about the kind of apps that would need this stuff, it could be a delivery app, think DoorDash, Uber Eats, Postmates, whatever one that is useful in your locale, or if you're using DHL, FedEx, UPS, various delivery services. All this information can be used to build a rich context of a mapping service that will go ahead and actually display where a delivery driver could be, and then you can notify the user saying, ''Hey, the user has entered in this location, they are a few stops away, track your progress here.'' Let's do that real quick. Now, as I said, if we get location again, it's going to pin that location service up there, and you can see the little arrow. Let's go ahead and let's put us on a freeway drive. We'll get the location and hopefully this is going to update to be somewhere different. We'll let it run a little bit. You can see we're starting to get a slightly different location. It's very subtle, but we can actually track this change, and this is going to require a small refactor. Our location service over here is working good. We're going to create a new state value over here, and it's going to be a string. It is going to be watchId, and then setWatchId. Instead of getLocation being an async function, what we're going to do is call geolocation.watchPosition. Now, this is the same value. Same thing across all the platforms, it returns a watcher. That watcher will go ahead and execute a callback every time it gets new location data. We'll give it no options because the defaults work really well and it'll get a callback with some position data. We'll keep it like that for now. Then inside of that, let's also do, ID equals await. Just to make sure I get all the type information. This is why I like having TypeScript in a project. You might not, but I do.

Running the App and Live Reload

Short description:

We can make changes here and see them in the app quickly by running npm run dev with live reload. The setup process involves configuring the server URL in the capacitor config and syncing the changes using npm or npx cap sync. This allows us to see logs in the native IDE and verify that the app is working. We can remove unnecessary links and focus on V plus React plus cap.

Then we'll just say set watch ID to ID. We'll comment all this out for now because we're going to do some stuff up here. Now, we'll just say const cords equals position.coordinates because that might be available. Then with that we can just say set lock to be, well, let's just go if cords set lock. Now we can safely check because this might not be available because position might not be available for whatever reason. This is looking good. We do have this watch ID, which we need to do a little bit of cleanup in our return function of our use effect. We'll just say geolocation.clearWatch, and then the ID is going to be watchID. All of this looks pretty good. Let's go ahead and we'll do a quick little run. We'll need you NPM run builds. That works well. NPM or npxSync. That's going to sync our assets with the native app. We can come back to Xcode. We're going to go ahead and we'll stop this and then we will re-run our app and then let this get up and running. Can you see how fast that was? I just want to make sure that that came through, great. Go ahead and make sure all this is killed in the background. Run that again, up and running very, very quickly, which is great. Let's go ahead and call getLocation again. We'll allow once one more time, and we're just going to let this call. Now, because we still are simulating a freeway drive, we could go ahead and get all of this data back, and then we could watch the values and update it in real-time. If, for some reason, we made an error, we're already in a place where I think you could see where the biggest bottleneck is, and that is the time from making a change here to getting it shipped inside of the app. There's already some stuff going on there. We can make the change here, we build, we sync, it'd be great if we can just go ahead and have this running locally. Well, we can. We're going to run npm run dev, I believe it's external, and we're going to just copy this, and then we're going to come over to our capacitor config. I believe what we do is we go ahead and we're able to override how this actually gets up and running, on our device using live reload. Now, if there's something that is confusing, what we can go ahead is we can look at the main menus. We can see our docs. We have a whole section of guides on how to do certain things. Let's see, Live Reload. Go ahead. We can go to our server. Just to make sure that we had this set up good, let's go to our Server field, we'll say URL is going to be, what is it? Let's just do some copy and paste. URL is going to be that. We'll set the clear text to true. That should be good. Now, we can come over to a new terminal. We'll do npm or npx cap sync. Again, to go ahead and sync everything altogether, we'll go back to Xcode. We'll run our build one more time. We're already connecting, but we can see something different here. We're using V and we're getting those logs in the native IDE, which is what we want. Now, we can come back over to our app, and let's go ahead and just make sure that this is working. We'll remove the links to Vit and the React docs. Perfect. This is looking good. V plus React plus cap, excellent. You know what, we don't need Vit because it's not that important at the moment.

Background Location Plugin and Implementation

Short description:

We remove the button and make iterative updates to our app. We explore the background location plugin provided by the capacitor community, which simplifies background tasks and offers a better solution for iOS. We install and add the plugin to our project. The plugin has a different signature than the built-in geolocation, using an add watcher function. We adapt the example code from the documentation to our use case. The plugin handles permission requests and provides an error callback. The location data is returned automatically.

See button, car, location. We're not doing anything with the button anymore. Let's get rid of it. We don't need that anymore either. Already, we have a very fast and iterative process. We can keep going with all of this. I'm going to move the pre outside of the card. Let's see, let's get the location. It'll go ahead, allow once. While that location is getting captured, we can go ahead, we can start to update parts of our apps. Instead of this, we'll say cap plus react, have all those values get changed. This isn't necessarily a feature of capacitor. It's really a feature of v and its hot module replacement. But you can see, as you are starting to access features and access native APIs, the feedback cycle from building your app and getting something running on device can be very, very fast. We don't need this anymore. We don't need this anymore, but there is something that we could do. If we look over at Xcode again, you could see, we cleared the watch because it was a use effect to get called. So we can go get that again. If we put the app in the background, locations are still being called, but it's not being done in a way that really is performant or is optimal for users. Instead, we have this capacitor community offering, which I think is a little bit of a better one. Background location. So, if you've done any background location before, any background services in say, React Native or Cordova, you know that they can be a pain. IOS is very particular with how they allow background tasks from happening. This plugin simplifies all of that and makes it so much easier. What we're going to do is we're going to set this up, and we are going to use some background location services inside of our app, and to do that, we're going to go ahead, and we're going to first off, install it, and then we're going to add it to our project. Let's go over, we'll stop the Dev Server, and then we will run that install command. This is a great way that we can see that even though there is core stuff that is provided by capacitor, sometimes, a community can provide different alternatives that are, in many opinions better, but also do something slightly different than make them also valid. Again, not saying that one is better than the other, it's just a different way of doing things that could be important. There we go. Instead of using capacitor geolocation, comment that out, we're going to use this background location services, which is community driven and supported. Thank you to this plug-in author. Now what we're going to do is we're going to take a quick look at the docs because we do need to see how this works, as it does have a different signature than the built-in one. Instead of doing this watch position, we have this add watcher. As a watcher, it has a callback function where we can say, ''Hey, this was not authorized,'' and then we can clear this after things are done. It's slightly different. The example is a little bit more verbose. We're going to adapt this to our use cases. We'll say background geolocation.AddWatcher, Now, let's go ahead and actually check what we've got here. We're going to more or less copy all of these options. Mostly because if they're in the docs, they are very helpful, but we're going to get rid of the comments. One of the things that they do that I really approve of is they request permission and they handle that for you. Then in the callback function, we get the location and then potentially an error. We can see, make sure I didn't open anything. We get the error code, which could be happening. Otherwise, we should comment out the console log at the actual error. But what's really nice is that they provide this open setting function. Again, building on top of what we've already provided with additional features, we're going to go ahead and say error, we'll just say if error console.error and then we can actually get the location data out of here. The location is not going to return us coordinates. It's actually going to return us a position dot. It's going to actually return us everything all automatically.

Modifying iOS Settings for Background Location

Short description:

We can modify the iOS settings to allow background processing and access to location data. By adding the necessary permissions and background modes, we can resolve errors and ensure proper functionality. Let's make the required modifications and test the app again. This will enable us to access privacy-sensitive data and get background location details.

What we can do is just delete that. In fact, we can just get rid of that all together and we can just say location, actually, it's not location, location is position. Yeah, yeah, that should be fine. We can go ahead and have that all set up and then the ID is still getting returned here and then we can clear that up. So long should be removed. But let's go ahead and try to run this again. Now we will run our dev command and then we're gonna run sync. Cuz again, it's found that background location is a third party plugin and it needs to get this set up. With our simulator open, we can go ahead and run this. And we can see this is actually an error. So geo-location.clearWatch is not a function, that's on me because I did not clear that up. What we can do, and I believe it's removeWatcher. Right, background geo-location, that's what it is. Background geo-location.removeWatcher. ID equals watchID. Let's get that up and running. OK, now that is good. Let's go ahead and just clear this real quick. And let's get our lock. Now you can see we do have other issues happening here. And this is going to be pretty common. So this is an exception. This is what happens in native when you do get an exception, do get an error. And I only get that because I know what we actually need to do. We need to make sure that we add some things like this UI background modes. So this is something that iOS does when you try to access background processing or background activities without declaring the proper permissions. So we already have this location when in use. We already have our location always in when in use, and then we have our UI background mode. So let's go ahead, and I'm just going to go ahead and copy this. And I'm just going to directly modify the iOS stuff because I already know where it is. Nope, not that. We're going to go ahead, and we're going to add this. And then we can stop running. We can go back just to verify that it's actually in there. Acquired background modes, you can see app is registered for a location updates when in background. That's what we want. That's what we need. Let's go ahead and run that again, and call get lock, and you can see this app has attempted to access privacy sensitive data without usage description. The usage must contain NSLocation always and when in use. Perfect. So we have when in use. We have always, but we do need one more. Let's go ahead now that too. Privacy location. Location. Always and when in use. Getting background location details. OK. Now one last time, which is very nice. It doesn't just let us to do this.

Creating a Note Taker App with Capacitor and Ionic

Short description:

We explore another API by creating a note taker app that interacts with the file system. This hands-on implementation with UIs will demonstrate the differences between using Capacitor alone and using it with Ionic. Let's run serve to see the blank Ionic app and improve its UI. In the homepage.tsx file, we create a map for notes and return JSON.stringifyNotes. We add the necessary variables and import useState. Finally, we install @Capacitor and the file plugin.

Let's go ahead and get that location data. Hey, getting detailed locations for demos. Let's go ahead and get that up and running. Now this is calling the background location implementation. Now we can go ahead, we can close this, and then we know that this is going on in the background and that data is already there. We don't need to go ahead and worry about it. It's being done in a way that is performing, it's using the correct APIs and correct permissions that is happening in the background. In fact, if we open up Settings, we can see where is our app settings? Maybe not. It might not be here in the simulator. It normally is on a real device, so we'll go back from that. But you can see it's still getting that location, it's still updating all of our bindings, and it's much faster and uses those correct permissions.

So I'm gonna stop for a second, take a sip of water. If there are any questions so far, please put them in the chat.

Okay, what we're going to do now, we're going to stop this, that way we can clear up those locations and not let this just run. What we're going to do instead of doing, instead of doing more with locations, because locations are not that impressive when we know we have them on the web, we're going to try to take a look at another API. And this is one that I have that we could use pretty regularly. In fact, we're going to go ahead and just, we're going to actually commit this add location demo. And we're going to go ahead and actually create another project. One that I am more familiar with. It might be a little bit more involved. I might regret it, but this is actually going to be a really, really fun one. So with that, the branching the project repo should already be up to date.

What we're going to do is we're going to create another project. Note taker and it's going to be a blank app type React. We're going to use this with Ionic, which is just a UI library. You could swap out Ionic with something like Tailwind or your own custom-rolled UI library of choice, preferably your own custom UI library of choice. I don't think everyone needs to use Tailwind, but you're going to roll this out with some UI. We're going to create a quick little note taker that's going to interact with the file system. This is going to give us a lot more hands-on implementation with how these UIs can be done. Then we're going to see how they differ across using Capacitor by itself without a framework, or they're using Capacitor with another free tool like Ionic. Principles can still be done. Like I said, you can still do this all without Ionic, but Ionic just makes it a little bit easier to do, in my opinion. For instance, that app looked fine, but we're going to make it look better than fine. Let's go ahead and run serve. This is just going to give us a blank Ionic app that we can see. It's already got some UI in here, and we don't really need these. We're going to go into our homepage, which I know is a homepage.tsx. This is still a React project at the end of the day, and all of this is just React components. We're going to go in and we're going to create a map. We'll say notes.mappnotes, and we're going to return. Just go ahead and just do JSON.StringifyNotes. Mostly because I know what we're going to be rendering here. All right. So we have some things that we need to add. Mostly the note and the notes. So let's go ahead and say const notes and then setNotes. Those are going to be some usedState, which is going to be an array with the type of any. We'll import usedState and then we also need to go ahead, and we're going to install atCapacitor, and we're going to install the file. I believe it's not hyphenated but just in case, let's go to a reference for the docs. We're going to grab the file system.

File System API and IndexedDB

Short description:

We install the File System API and request permission to access the file system. We then read the directory of files and handle the case where the directory does not exist. We use IndexedDB to mimic a file storage API and perform various operations such as getting the folder, directory, and size. We create a function to make files and set the notes array. Capacitor is fully async and promise-based.

Yes, it's not hyphenated. It's going to install File System API. This will let us have access to obviously the file system in a way that is very familiar if you've done anything with Nodes file system API. So let's go ahead and we're going to import from atCapacitor File System, and we'll import the file system class. Now we're going to use a lifecycle hook that is specific to Ionic, not necessarily specific to React, but it's familiar, well actually, let's just use the React equivalent for now. It's going to have no dependencies, back towards empty arrays that are null, what we're going to do is, we're going to go ahead and just request permission to access the file system. So we'll say 콘t, initfs is going to be an async function, or we'll say filesystem.requestpermission, and in fact, let's move this out over here, so it doesn't get executed every time on useEffect, and we'll just say initfs. See how that goes. Okay, filesystem has been taken care of. And then we're going to go ahead and we're going to say read directory. Now read directory is going to be another async function. And this is going to be where we go ahead and actually read the directory of files on our filesystem. So we'll say await filesystem dot reader. And we're going to give it a directory, which is going to be a directory dot document. Now this directory is actually an enum coming from the filesystem API it's basically giving us a type safe way to say go fetch me something from a documents directory. And then we're going to give it a path, which is going to be the actual folder that we want to read. In this case, we're just going to read the notes and then we're good. But if we're in a situation where that directory doesn't exist, this is going to error. And in fact, let's just go ahead and we're going to create another function here called make dir, and make dir is gonna do exactly what it sounds like. It's gonna call make dir and if that directory exists, make dir will just say, directory already exists, nothing to do here. It'll reject that promise and then it'll go ahead, we'll kick off a read dir. So let's say init file system and dot then we'll go ahead and we'll say, make dir dot then, we'll go ahead and say this dot or read dir. Let's go ahead and let that save. The prop directory already exists, you can see that we've already handled that properly. With this dot then, we'll go ahead and just say no up, actually how do we wanna do this? Oh, we'll just go ahead and chain these all together. Why not? Because async just makes it so much easier. Await mate, await. Make dir await read dir. And then instead of calling that we'll just say init. All ready fine, this directory being a problem, or it exists, because we can catch this up here. Console.log dir mate, and then instead of, you say, console.log dir already existed. Cool, already good, things are looking great. In fact, we're going to just clean this up real quick, so that way it's easier to read. Now, with all of this together, let's go ahead and just say, console files equals this, and let's just console.log all of the files that we got out of this. The files give us an array of, or gives us an object of files that is an array. Let's make this even better, because destructuring exists, and we can see that we get an array of files out of this. There's no files here, the directory does already exist though, so we have nothing to create here, but we do have access to a storage in a browser that doesn't support a storage API. What's going on here? Well, turns out, there is a storage API inside of the browser, and it's very clear a file storage by a folder. We're actually using IndexedDB to actually mimic a filesystem API, which when I have described this to people, they thought that is a terrible hack, but it works. IndexedDB is actually very capable and provides a lot of cool features. We can get all the time of a, we can get the folder, we can get a directory, we can get the size of the directory. A lot of cool things that can be done in here that just make it feel so right to do it in IndexedDB. Let's go ahead and we're going to, for the sake of a demo, we're going to go ahead and do all of our files being made up in here. We'll just say, set notes and it's going to be our files array. We're going to check in on a browser, everything is looking good. We're going to create one more function here, and that is going to be const make file. This is also going to be an asynchronous function. Capacitor, if you can't tell by now, is fully async, all promise-based. You will see async quite a bit inside of your projects. But we're going to have some data.

Creating and Storing Files

Short description:

We create a new file in the directory with the specified data and encoding. A button is added to call the function. An error occurs when a name is not provided for the file. Each file is assigned a unique key. The data is stored locally and is not accessible to third parties. The same process can be applied to iOS.

At this point, we're just going to make it an empty string. Or in fact, let's just say it is, hello demos or demo. Then we're going to call awaits. File system. Then we're going to go ahead and call write file. Write file is going to give us a quick way to just say, making a file in this directory with this data, with this encoding. If you've done write file synced in Node, this works the same way. We have a function. We'll say the data is going to be data. We'll say the directory is going to be directory.enums, or.documents, path, notes, and then encoding. I don't know why that's happening..utfa. Our file system is going to go ahead and then it's going to write a file for us and then we can just say then, reader, because we just want to reread the directory after that file gets created. In fact, let's go ahead. Yeah, that'll be fine. Actually, we're going to go ahead and we're going to change this. It'll be pivot real quick. We'll just say date.now. This should be fine. Let's go ahead and save that. Then we're going to add a button that will go ahead and let us call that. If you don't mind, this is just going to be some quick ionic, slot equals end ion button on click. Well, actually, before we do that, let's import the button. On click, it's going to be add make file and then ion icon, icon equals plus, what's it, add. Let me figure out where the icon comes from. That should be good. Now, our documents folders there, we have our button up here in the DevTools, let's go ahead and create a new one and see what happens. The supplied path is a directory. What does that mean? We have to give it a name. I forgot about that part. This is actually a really good one. You do need to add a new name whenever you make a file. What we're going to do here is change surrounding to be some backticks. We're going to actually just pass in data, and then give it a dot txt. Then we'll call that again and see what blows up. Each item should have its own key prop. That's totally fine. We're just going to do some type coercion here, it's going to be a file, which is from capacitor itself. You can see all of the different data automatically available here and we're just going to give it a key of note dots, o name. This is working fine. Let's go ahead and add another one. Each note in here is its own thing with its own bit of data. Now, if we go ahead and refresh this directory, you can see all of that data getting stored here. And then we can see all of the different pieces in that actual document. So this becomes very compelling as you try to build rich apps that are going to be complex. Now, the great thing about local store, about IndexedDB here, is that this is only available to local hosts. This is only available to me. It will never be available to a third party, which is by design and very good. Now, the same thing can happen if we go ahead and we run a build and try to do this on iOS. Now, the build is going to take a little bit longer because it's a little bit more involved. But if we do the same processes as before, so you'll add iOS.

Capacitor in a Project: Filesystem API and Wrap Up

Short description:

The Filesystem API is a simple API for reading files, getting directories, creating permissions, and more. CapacitorJS.com is the main homepage for Capacitor, with examples, references, and documentation. The Capacitor community has developed high-quality plugins, such as Photoviewer, Barcode Scanner, and Firebase Analytics. Many companies, both in the US and internationally, use Capacitor for their internal and external apps. Capacitor allows you to build a full native app that can access APIs through JavaScript. The native project is accessible and can be customized if needed. Thank you all for attending the workshop.

That's going to go ahead, have that all up and running Ionic cap, run iOS, and we're going to use some of ionics helper functions internally to get this up on our simulator faster than if we were to do it by ourselves. Our simulator will go ahead, it's going to run that native target. It's going to deploy it to our device. Here we are on our device. We have our dev tools open here in the browser. Let's go to our simulator again, and let's go ahead and run this. So this is giving us a very different return value. You can see here we have, and in fact, let's go ahead and just fix this real quick. The P probably not the most easy to read parameter here, and we're going to say null two, and then much better, easier to read. Now where we see we have this URI, what do we have in here? We have a path. Now what this does is give us a different context for where this gets read and where this gets loaded up from. Here we're reading from our device's actual sandbox location service. So as you build out an app and you want to store data for a longer period of time, that data can be stored inside of the device's own storage mechanism and not be subjected to potentially the browser clearing out that cache for whatever reason. The index DB stuff could potentially be cleared out, but this will never be cleared out unless you go ahead and go over to your Settings, and it's still not showing up there. Okay, well, whatever. Unless you manually clear this out from yourself, there's no way that this will ever disappear. All of these are going to be perfectly existing in this app unless I delete the app itself. So we've already gotten a lot of this up and running. I'm feeling pretty good at this point. It's been roughly two hours and we've already had a person dropped from the workshop. The Filesystem API is a pretty simple API. A lot of it, if we look at the code, is more about just doing some orchestration. Reading the files, getting the directory if it doesn't exist, creating the permissions, pretty standard stuff. So this is all something that anyone could build and have their own pretty nice note-taker app really quickly. What I'm going to do is go ahead and I'm going to commit, let's see, second demo, and we'll say hub creates, we'll call this jsnation-demo2, git push origin main, and then hub browse. I'm going to drop this also here inside of chats, so that way you can all take a look at that, and I'll have the other demo prep as well, but what we should be doing now is, as we're at the kind of two hour mark, do some quick wrap up, because I know it's a long day for probably some of you, considering the time zone, so it won't take up the rest of your evening. But, the process of getting Capacitor in a project, fairly simple, fairly low effort, we installed these two dependencies, and we init the project. If you're just trying this out on your existing app, make a new branch, give it a try, and see if it's something that you could benefit from. If you want to learn more, we have CapacitorJS.com as our main homepage, there's plenty of good examples and references inside of that one landing page, and then we have the docs for more help on how to actually go ahead and implement some features, get some guides, or look at actual plugin documentation. For that background location example, we have the Capacitor community to thank for that, this is kind of our, are sanctioned third-party plugins from authors who have dedicated to maintaining them, you can take a look at some of those plugins in there, they are all very high quality and maintained by our lovely community, if we actually go back and take a look at everything minus all of the search queries. There's a good number of plugins in here, the Photoviewer, Barcode Scanner, camera preview, Firebase analytics, you've got a lot of stuff in here if you want to actually use this. AdMob, even for getting some revenue from ads inside of your app, which is something people apparently do quite a bit. So definitely check out these resources if you want to get started in doing some stuff in Capacitor. I only say this just because I know a lot of people get very weird about using open source tools, but there are plenty of companies using Capacitor right now in production. Some very big companies here in the US, some very big companies international. They're all using Capacitor, whether it's mostly, whether it's internal apps or external, internal apps or external apps, they're using Capacitor. In fact, some of these food-branded ones are using it for their whole entire customer ordering experience which is really, really cool. Capacitor hopefully by the end of this should show that you don't have to write a lot of code to get a lot of functionality. You can have a full native app that still is mostly web and can still access those APIs through JavaScript which is very nice. In that if you do need to do some stuff with the native project, it should not be something that gets hidden and tucked away in the corner, it's there. You should be able to access it and then provide some customizations if you need it. But that's all I have for today. Thank you all so much for hanging out with me for the past two hours. I hope you get some value out of this. If you need any questions, I will be in the discord for a little bit just to hang out. I'll drop all the links for the demos that we had and some of the resources. But you have all been wonderful. Thank you so much.

Watch more workshops on topic

GraphQL Galaxy 2022GraphQL Galaxy 2022
156 min
Hands-On With SwiftUI, GraphQL, & Neo4j AuraDB
WorkshopFree
Bring the power of graphs to iOS mobile app development in this hands-on workshop. We will explore how to use the Neo4j GraphQL Library to build GraphQL APIs backed by Neo4j AuraDB and how to integrate GraphQL into an iOS app using SwiftUI and the Apollo iOS GraphQL library as we build a news reader mobile app.
Table of contents:- Intro to Neo4j AuraDB- Building GraphQL APIs with the Neo4j GraphQL Library- Intro to SwiftUI- SwiftUI + GraphQL
PrerequisitesTo follow along during the workshop attendees will need a Mac laptop with a recent version of Xcode installed. Some familiarity with Swift and iOS app development will be helpful, although not required.
React Summit 2022React Summit 2022
92 min
Bringing your React Web App to native with Capacitor
WorkshopFree
So, you have a killer React app you've built and want to take it from your web browser to the App Store. Sure, there are a lot of options here, but most will require you to maintain separate apps for each platform. You want your codebase to be as close as possible across Web, Android, and iOS. Thankfully, with Capacitor, you can take your existing web app and quickly create native iOS and Android apps for distribution on your favorite App Store!
This workshop is aimed at intermediate developers that have an existing React application, or are interested in mobile development with React. We will go over:
What is CapacitorHow does it compare to other cross-platform solutionsUsing Capacitor to build a native application using your existing web codeTidying up our application for distribution on mobile app stores with naming conventions, icons, splashscreens and more.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2021React Advanced Conference 2021
21 min
Building Cross-Platform Component Libraries for Web and Native with React
Building products for multiple platforms such as web and mobile often requires separate code-based despite most of the components being identical in look and feel. Is there a way where we could use shared React component library on different platforms and save time? In this presentation I'll demonstrate one way to build truly cross-platform component library with a unique approach of using React & React Native in combination.
React Advanced Conference 2021React Advanced Conference 2021
27 min
Limitless App Development with Expo and React Native
App development is hard, React and Expo make it easy!It's never been simpler to build and deploy powerful mobile apps with incredible features to both Android and iOS users all over the world.We’ll discuss building and deploying mobile apps seamlessly from the cloud using EAS, creating powerful dev clients (like browsers but for mobile app development) for testing your app, pushing OTA updates instantly to users, and much more — no native experience required!
React Summit 2022React Summit 2022
7 min
How to Share Code between React Web App and React Native Mobile App in Monorepo
Usually creating web and mobile apps require different tech stacks, and it is pretty hard to share code. This talk will show how I added a React web app and a React Native mobile app in the same monorepo using Nx, and how I optimized codeshare between react web app and react native mobile app.
React Summit Remote Edition 2021React Summit Remote Edition 2021
35 min
Building a Mobile App with Expo, EAS, and React Native
It has never been easier for React developers to build native iOS and Android apps. In this talk, we'll see how quickly you can ship your app with Expo open source tools, Expo Application Services (EAS), and React Native. We'll also discuss some of the recent improvements we've made and what's coming up next.
React Summit Remote Edition 2021React Summit Remote Edition 2021
18 min
React Native Architecture at Product Hunt
I'm going to showcase the React Native architecture we use in our new mobile app at Product Hunt. What we learned, among the way. How we moved what we know from web to mobile. Topics will be designing reusable React components, GraphQL, routing in the app, application lifecycle, keyboard controls, toast messages, and others.
React Advanced Conference 2022React Advanced Conference 2022
22 min
The New Architecture Is Here… What Now?
The React Native new architecture has been "coming next year!" since 2019 - but, this time, it’s actually here! So… what now? What do we actually need to do, to benefit from this all new shiny tech? In this talk, I want to provide some insights and in-depth analysis of the current state of the migration path to the new architecture, to help you and your team evaluate and estimate when and how and how long it will take you to get to the next level!