How to Store Data on macOS Keychain with React Native Bridge
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Building React Native Apps for Mac course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Building React Native Apps for Mac, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
[00:00 - 00:23] So now that we have seen a little bit of the internals of React Native, namely how to manipulate the root container or how to mount our React Native view, now has come the time to talk about exposing native functionality to the JavaScript side of things. We're going to take on this task by adding persistence to our app.
[00:24 - 00:38] Now React Native has some built-in persistent mechanisms, but we don't want to just store the data, we want to securely store the data. So in order to do this, we're going to take our application data and put it on the macOS keychain.
[00:39 - 00:56] A lot of the knowledge on this lesson is going to be very important for the next lessons, so it's better to pay attention. Now we talked about the React Native bridge before, but it's time to clarify it a little bit more.
[00:57 - 01:07] So the bridge allows the communication between the JavaScript code and the native code. And this also allows us to create private APIs.
[01:08 - 01:21] We can create our own functionality and expose our own methods that talk directly to the native code. So we're going to start by adding the keychain framework.
[01:22 - 01:43] We're going to go back to our VS code instance, and I'm going to open the pod file, specifically the pod file on the macOS folder. Here you'll see that under this line it says pod specifically for the macOS target, and here's where we're going to add a new dependency.
[01:44 - 01:53] We're going to add pod keychain access. So this is a native library.
[01:54 - 02:14] It doesn't have any React Native binding, but this is the one that we need to add data into the macOS keychain. So with that line on my terminal, I'm going to go into the macOS folder, and I 'm going to do a pod install.
[02:15 - 02:37] Great. So now that the dependencies install, I can go back into Xcode, and I'm going to create a new file on the macOS folder.
[02:38 - 02:46] This is going to be an Objective-C file. It needs to have the .m extension, and I'm going to call it building apps native.
[02:47 - 02:59] I'm going to add it to the macOS target, and I'm just going to take the code from the lesson. Great.
[03:00 - 03:14] So let's go over this very briefly. So first of all, we're importing the bridge module, which is necessary to -- it 's going to take our code.
[03:15 - 03:19] There's not necessarily code that can run. These are macros.
[03:20 - 03:42] So it's going to take whatever we put in here, and it's going to actually generate native code that bridges or that is exposed to the JavaScript and can call the native element or the native functions that we want. So we start by declaring an interface with the RCT external module function.
[03:43 - 03:56] And we're passing here a class and the base of that class. So this one we haven't created yet, and we're going to create this class in Swift, but for now it's going to be there.
[03:57 - 04:05] Afterwards we're going to declare two external methods. We're going to declare a keychain write and a keychain read method.
[04:06 - 04:16] For the keychain write method, the first parameter is going to be a key. The second parameter is going to be a payload, which is a string.
[04:17 - 04:32] And we're going to take a resolver, which is of this type, RCT promise, res olver, and a rejector. So the first two are quite straightforward, right, is the key of the object I want to save and the payload.
[04:33 - 04:44] But the resolver and the reject blocks are internal to react native. And they basically allow you to interact with promises on the JavaScript side.
[04:45 - 04:58] For the keychain read method, it's going to be similar. It's going to take a key, which will be the key that we will extract from our keychain.
[04:59 - 05:06] And again, a resolver and a rejector method. Now the payload or the signature of this functions is a little bit funny.
[05:07 - 05:15] Don't worry too much about it. Basically this kind of maps to how objective C and Swift pass parameters.
[05:16 - 05:23] And we will see in a little bit how they map to the actual Swift files. Great.
[05:24 - 05:34] So now we can move into writing our Swift code. Before we do that, we're going to open, we're going to save this file and we're going to open the bridge one more time.
[05:35 - 05:43] And in here, I'm going to add one more line. It's called import, react, RCT, bridge module, right?
[05:44 - 05:48] So by default, this, oh no, it's already here. Sometimes it might not be there.
[05:49 - 05:56] Are you ready? Because I pasted the code before, I already put it in here.
[05:57 - 06:06] But you have to make sure that this one is exposed. So back on my macOS folder, now I'm going to create a new file.
[06:07 - 06:13] And this is going to be that Swift counterpart. And I'm just going to call it the same building, apps, native.
[06:14 - 06:20] And again, it needs to go on the macOS target. And now we have it in there.
[06:21 - 06:26] I'm going to take the code one more time from the lesson. Great.
[06:27 - 06:43] So first I'm going to import foundation, which is required because it contains the NSO object. And I'm going to import our path or dependency that we just added, which is the keychain access.
[06:44 - 06:52] So I'm going to create an instance of the keychain. And I'm going to give a name for the service that we're going to run.
[06:53 - 06:58] I'm just going to call it building apps. And then I can declare my class proper.
[06:59 - 07:10] So again, we see this objective C header, right? Which means the Swift class will be available to the objective C context as building apps native.
[07:11 - 07:27] So it's important that the name that you declared on the object C file that will generate the code matches both of these names. Then I declare my class the building apps native.
[07:28 - 07:40] And it needs to inherit from the NSO superclass. Inside of it, we need to have one static function, which requires main queue setup.
[07:41 - 07:47] We'll go over queues in a further lesson. But for now, you can just copy and paste this code.
[07:48 - 07:56] And then we can actually start implementing the two methods that we declared before. Again, they need to be exposed to the objective C context.
[07:57 - 08:04] But afterwards, they're just normal suite functions. So the first function is the keychain write, as you saw before.
[08:05 - 08:19] Now this is a little bit of Swift notation that is, I can actually put aliases to the parameters that I pass. And it's just kind of weird syntax here.
[08:20 - 08:30] If you notice, actually, this is the name of the method and this is the first parameter. But when we go into the Swift code, the name of the method is apart.
[08:31 - 08:42] So we kind of have to have this null alias for our key for first parameter in the method. So it takes the same parameters.
[08:43 - 08:54] It takes the key and a payload or resolver, which is the RCT promise resolve block and our rejector. The keychain read method is again very similar.
[08:55 - 08:58] It's the key, the resolver and the rejector. Great.
[08:59 - 09:11] So now for the implementation of our keychain write method, it's very, very simple. The keychain dependency makes it super trivial to add data to the keychain.
[09:12 - 09:22] So on the keychain object, we just invoke it or we just access the key. It is the key that was passed in the function as a string.
[09:23 - 09:32] You can convert freely from NS strings to Swift strings. And we're just going to say that's equal to the payload.
[09:33 - 09:41] Once that's done, we can tell the resolver that the data has been properly inserted. For the read method is the opposite.
[09:42 - 09:49] I'm going to extract the value by accessing the keychain at the key. And we're going to resolve.
[09:50 - 09:59] Actually, I don't need to return anything. We're going to resolve the value by passing it back to the resolve function.
[10:00 - 10:04] Here you can see, for example, this is how aliases work. You don't need to do this, right?
[10:05 - 10:18] But in case you might have a duplicated variable, you can just put an alias for your parameter. So right now, if I would hit compile, the app would compile and it would run.
[10:19 - 10:24] But there is still no way to access these functions from our JavaScript site. We still haven't seen it at least.
[10:25 - 10:29] So in the next lesson, we will learn how to invoke our native functions.