Create Cross-Platform Apps With Expo
with Brent Vatne
If you want to create an app for iOS, Android, and the web, why not do it all from the same codebase? Brent Vatne teaches us how Expo makes it possible!
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON: Hello, everyone, and welcome to another episode of "Learn with Jason." Today on the show, we have Brent Vatne. Brent, thank you so much for coming on.
BRENT: Yeah, absolutely, thanks for having me.
JASON: Yeah, yeah, of course. So you and I have met. I emceed Chain React, or co-emceed Chain React, was it last year?
BRENT: Yeah, feels like a long time ago.
JASON: Feels like a decade ago. Wow. So we met there, had a great time. It was out in Portland, so I got to play host a little bit in the home city. And since then, I've been passingly familiar with your work, because we sort of work on different ends of the spectrum, or so I thought until we started talking about this episode. So for those of us who aren't familiar with your work, do you want to give us a little bit of a background?
BRENT: Sure, so React Native has been out for something like five years now. When it was first released, I thought it was an awesome project, so I jumped in and started contributing right away. And kind of got into -- I was in consulting at the time. So got into some consulting projects around that and got into eventually working on this project called Expo, it was called Exponent at the time I started working on it. So what Exponent is, or Expo, basically like -- I like to explain it as rails for React Native. So we do a lot of the work that you might otherwise have to do manually. Kind of just put you on some convenient guide rails to help you speed up getting your app shipped, building the first version of your app, as well as continuing to iterate on it and grow it and so on. And so it's this set of not only libraries and SDK, but also -- nice.
JASON: Thank you for the sub, Spencer.
JASON: Interesting. I didn't realize you could even do that.
JASON: Yeah, that's super cool. So this might be a little bit of a loaded topic, but I have a few questions, and I think these will potentially be shared by the chat. Remember, if you have questions, you can drop them, as well. But I'm always kind of curious, what do you think the benefit is of -- like why would I build a Native app, you know, especially as the capabilities of the web are expanding, what's my reason, or what do you think is a good driver? Or maybe actually a better question, what's the heuristic? At what point would you say, yeah, you should totally build a Native app now?
BRENT: Well, prior to that -- one thing I think we want to do with Expo is maybe eliminate that question. And you can instead build a Native app and website at the same time, so you don't have to think about building all of these completely separate codebases from scratch. I think it really depends on the product that you're building. Something like Netflix, for example, the experience that you get from using the app, the ability to do things like push notifications, which still aren't supported for PWAs and iOS, and, Apple is going to continue to hinder this, I think, for the foreseeable future, unfortunately. Something like push notifications, or maybe their own custom video player with the sort of capabilities maybe you would not be able to ship with the web, I think some of those changes a little bit as the web gets more mature, but, ultimately, I think native platforms will continue to be for certain use cases several years at least ahead of the web, as the web has to adapt to new technologies, new APIs, and sort of adapt them, include them into standards, ship that in a new version of the browser, and so on. So there will always be this game of chase, basically. So I think that's one category of app. This is just, like, things that require some novel or cutting-edge API, or really performance-oriented code.
JASON: Sure, okay, cool.
BRENT: I think also there's just general sort of typical information app. I would say you should probably have a website in these contexts most of the time. But I think that at least in my case, I typically enjoy the experience of using an app on my phone a lot better than a website. And as much as I love the web, and we're building Expo in a way we're trying to recreate the developer experience of using the web on iOS and Android and make it work everywhere, but at the same time, there are just limitations to what you can accomplish in a web browser on mobile currently. And simple things like having really nice gestures in your app are just not really feasible on the web currently. And using some of these built-in platform tools, as well, that can help you get your app into kind of a better state without having to rebuild on top of, you know, UI libraries that are like Bootstrap, which is kind of out of fashion these days. If instead you use the design language of the platform that you're working on and leave some of those primitives --
JASON: So I think that's something that's come up a few times, especially in kind of design circles, right. Is when you look at native apps, the level of polish that you see in a typical native app seems to be just kind of far and away above what the default would be for like a web experience. So you're saying that's coming out of the native apps kind of laying a bunch of groundwork for you?
BRENT: I think that is a big factor, for sure. And then there's also a matter of the ceiling. I think that maybe the end result ends up being similar of the average between the floor and the ceiling. And on the web, the familiar is, you know, plain HTML page with blue anchor tags, black text, white background. And on native, the floor is like using these kind of built-in components that you get like UI navigation controller, which provide a pretty nice experience out of the box for you.
JASON: Sure. Okay, yeah, yeah. That totally makes sense.
BRENT: Even the ceiling of the web, you wouldn't be able to recreate some of the things that are available on native.
JASON: Yeah, and I've definitely found myself in a lot of stack overflow threads, like how do I recreate this finish of the iOS experience. I remember when pull to refresh was like a game changer, right, and everybody was trying to figure out how to recreate that on the web. And so I can definitely see what you're saying about native is just going to be a little bit further ahead, because you've got more fine-grain control. You're not waiting on standard support. You're not waiting on multiple browser vendors to implement a feature. Okay, so that totally makes sense. So another question that came in is, I think we can talk a little bit about performance specifics later on, but when Expo, you mentioned that Expo allows us to use the same code base. Thank you for the sub, Nate Norberg. I'm bad at Twitch names. I'm reading them very phonetically.
BRENT: Honestly, I was impressed. I thought you read that and knew what that name was. Impressive, Jason.
JASON: That's just me making stuff up. Yeah, I think what's really interesting about Expo is you're presenting a sort of "have your cake and eat it, too" opportunity. Where we want to be on the web, but we want to have native apps because of all the reasons that you mentioned. And you're saying we can use the same codebase to make that happen. So what is the codebase that we use? Is that -- are we doing that with React Native?
BRENT: Exactly, yeah.
BRENT: So React Native, React Native itself is kind of an increasingly small core library that includes kind of these core set of primitives, which sometimes I think is an overused word, but I think applies nicely here. Where the primitives are things like view and text and image, something like a scroll view, so like a scrollable view. A style sheet, right, some way to do styling, layout system. And then a plug-in system for how you can extend that and add your own components on top of that. And then there's also, of course, this run time that will allow this code to execute on the platform that you're running on and render down to native views on those platforms. If you would translate to a div on the web, or maybe in the future, like a switch-view component.
JASON: Okay, very cool.
BRENT: Sorry, just to extend from that. So that's kind of the core of React Native. And what Expo kind of provides on top of that, going back to like the rails comparison, you know, that's kind of like what you get with maybe using Ruby on its own with, like, a simple web server or something like that. You have all the things you really need to build an app, but you might end up rolling a lot on your own after that. So we're kind of trying to provide that more cohesive experience.
JASON: Got ya. Okay, very cool. Yeah, I think I am really excited to see how this works in action. So if you're okay with it, let's just switch over and start building something. What do you think?
BRENT: Sounds good.
JASON: Okay, before we get started, let's do a quick shout-out to our sponsors. We've got live captioning on the show today, go to LWJ.dev/live to see that. Live captions are here. These are provided by White Coat Captioning, thank you so, so much for White Coat being here today. Netlify, Fauna, Sanity, all chipping in to make this show more accessible to more people, which we very much appreciate. Also, make sure that you go and follow Brent on Twitter to learn all the things about Expo and all the things that you're working on. Okay. So this is Expo, right, here's Expo here, and we are going to be -- we're going to put this theory to the test. Fastest way to build an app. So if I want to get started, what should I do?
BRENT: You can scroll up to the top here. Or underneath that, wherever you want, and there's a "Get Started" button. And this will guide you through the high-level steps. So you need node, Expo CLI.
JASON: I think I have Expo CLI. Let me check. Yes.
BRENT: Looks good.
JASON: Do I need to check -- is it dash, dash, version? Is that the right version?
BRENT: That will do, yeah.
BRENT: Subscribers are rolling in today.
JASON: No kidding. Oh, oh, oh, look at this, Michael Jolley gifting everybody subscriptions. Thank you very, very much. We came up with a name for this, what was it, Boop Crew, welcome to the Boop Crew. Make sure you spam those Boops. You can, if you work together as a team, bury us. Yeah, feel free to make that happen. And while you do that, I am going to initialize this project. So let's Expo, init. I'm going to move it up, so when they bury us we can still read. I just choose any folder name?
BRENT: Or hit Enter and it will prompt you for a name. So let's choose the blank template here.
JASON: Blank. We'll name this -- what are we doing today?
BRENT: We're going to take the corgi app you built in the Firebase episode, and we're going to make it run in React Native.
JASON: Very cool. We'll call this -- what did we call that app? This app was called Corgi Photos. We'll call this Corgi Photos Expo.
BRENT: Sounds good. So this will basically just clone a template project, extract it, and that's running yarn for us, which, you know, it is what it is. It takes some time. We know the ecosystem that we're in.
JASON: Indeed. We know what we did.
BRENT: None of us is innocent in this case.
JASON: Okay, so now we've got our app up here, is built. Into Corgi Photos Expo, and let's take a look at this. Okay, does kind of sound like a convention where people share their corgi photos. Kind of into that. I'm into an expo hall where people share corgi photos.
BRENT: That's true.
JASON: Let's see. This looks pretty clean. We've got an assets folder, which has what looks like pretty basic stuff. An app.json, and I assume this is all meta?
BRENT: Yeah, this is the stuff that's used to configure your project. So things that aren't code.
JASON: Our app icon.
BRENT: Orientation lock, or not lock of the app. So this is just some basic stuff that you might want in every app there And app.js is where the real stuff is happening.
JASON: Okay, excellent. Just to run through this real quick, because a few things are different when looking at React Native. So a couple things I would expect to see in here would be divs, but we can't use them in React Native, is that right?
BRENT: You could if you had a component that was only going to run on the web, which you could by specifying the web.js extension, but really you'd stick with the view component, which wraps divs in a way that is just normalized style and things you'd want to just keep.
JASON: Nice. If I'm thinking about this as a web developer coming into this, if I'm going to write in React Native, the view is my div, my generic wrapper component I'd use, is that correct?
JASON: Then we use text instead of paragraphs. Status bar, this is an Expo-specific thing, right?
BRENT: Yeah, so there's one included in the React Native core itself. This one from the Expo package is a slightly different API, and it's meant to handle themes better, so if you have a dark theme or light thing, it will do the right thing out of the box. Like I was saying, the React Native package is becoming smaller and including just the fundamental primitives that are needed for these apps. And I imagine status bar will be something that will be removed at some point, as well.
JASON: Okay, cool. Then we have some basic, basic styles. And you said we're going to take this app here, which is a list of corgis, and we are going to make those -- make that into a native app. So if you want to know how we did the Firebase part of this, you can watch the episode or check out the source of this demo here. And then you sent me some code beforehand, so this -- you said this is to just kind of generalize some of the code that we did in the Firebase app?
BRENT: Yeah, I basically just didn't want to complicate any of our app code with Firebase-related code, so that when people are following along, they can kind of treat the data as coming from somewhere and just see it as the usage of a hook.
JASON: Got it.
BRENT: Interacting with Firebase.
JASON: So the basics of what we're doing, we're setting up our Firebase app. These are the public keys that you use to connect and make sure it's pulling from the right place. We set up authentication in our database and grabbed the corgis collection, which is what's got these little buddies on it. A function to sign in, a function to sign out, a function to create a new corgi. A function to get the user, and a function to get all of the corgis. Is there anything -- oh, and then here, you said this one we might not have time to get to that, so we'll talk about this if we get there.
JASON: So do you want me to copy/paste this into the app?
BRENT: Yeah. Let's just drop this into db.js file in the root of the app.
JASON: Okay, let's go db.js, and now we have, in theory, our whole everything. It's all set up.
BRENT: Yeah, it's all set up, and we can do something with that shortly. So --
JASON: There's a quick question about the styling. Is there an alternative for styling that is more like plain CSS?
BRENT: The response there is I believe correct, that you can set up emotion. You can use something like style components, I think, which gives you a more -- I guess the thing that makes it not look like CSS here, if I'm understanding correctly, is it's camel cased rather than kebob cased.
JASON: Yeah, I mean, emotion would give you the CSS template tag, where you could just wrap actual CSS declarations in a template literal, sort of like this. Oops, not like that. Come on, stop helping.
BRENT: You're too helpful.
JASON: Like that sort of thing would work in this, if you used emotion. So that would be the way to do that, I think.
BRENT: Yeah, that should be doable. I, personally, don't do that. I can't tell what the best way to do that is, but there you go, emotion native.
BRENT: Fine solution for that.
JASON: I'll grab a link for that, so it ends up in the show notes. Emotion native. This will give you the ability, there you go, plain CSS. All right, so --
BRENT: In line CSS and js.
JASON: Exactly, exactly. All right, so we've got the database at the ready, and I believe right now this is like an Expo kind of placeholder page, right? So what should I do next?
BRENT: Well, let's run the app. Let's open it in simulator and see what we have.
BRENT: Switch back. Yep.
JASON: You told me to do -- you want me to open in the web, or which one?
BRENT: Let's run yarn start, and that will just open up, indirective prompt kind of like when you run jest. Looks like you're getting a warning.
JASON: Opened in the wrong window, so let's open over here. Okay, so here is -- this is kind of cool.
BRENT: Yeah, so this is an optional web UI. We used to have two separate tools, where there was one that was a tool like this, then we had a desktop. So terminal tool like this one. But we wanted to consolidate those, and so by default, when you run yarn start, if you haven't run it before, or haven't disabled this, then it will open up the browser for you. You can press Shift and D and that will stop it from automatically starting in the future and just run everything from terminal instead.
JASON: Nice, okay.
BRENT: So if you type question mark now, you can see --
JASON: Give me just a second here. (Coughing).
BRENT: No problem. Robutussin.
JASON: Try as I might, I still can't breathe coffee. So I have my list of commands. So we can press A to do Android.
BRENT: Rick said I for iOS, W for web.
JASON: Oh, I missed that part.
BRENT: Why don't we try opening in web first, I think there are a lot of web people here. Just to show that. So it actually starts Webpack.
JASON: Okay, there we go. Look at it happen.
BRENT: So now we can go back to the terminal and press "I" and that will pop up the simulator, which might be in another screen for you.
JASON: There it is.
BRENT: Perfect. It's defaulting, I guess, to iPhone SE on your machine. You can change that, but probably doesn't matter. Probably best for the case of streaming right now, so we don't make the fans take off.
JASON: I've firmly strapped down this computer, so it doesn't fly away while I try to stream and run iOS simulator.
BRENT: So that just opened up the Expo client app in the simulator. And so I think it's worth taking a moment to explain what the Expo client app is.
JASON: Yes, please.
JASON: Does that mean that these are both running at the same time?
JASON: Oh, interesting. So then does that mean, just kind of playing this out a little bit, if I start to make edits, look at it go. So we just edited a native app and web app all in one codebase, and it's live-updating. That's slick. That's really slick.
BRENT: Yeah, it's pretty nice. One thing that isn't perfect yet is that the web, where it's doing live reload rather than fast refresh --
JASON: Sure, sure, sure.
BRENT: For people who aren't familiar, fast refresh is like the newest renamed version of hot reloaded that actually works. And so on iOS and Android, it uses Fast Refresh, and it's beautiful. It just reloads the code that you changed, works great, preserves the state. In a lot of cases, most cases, and it's great. Whereas on the web, it took a little bit longer, because it had to actually reload the page. But, yeah, so we can just keep going like this, I guess.
JASON: Yeah, I'm ready.
BRENT: All right. So we could start off by maybe just getting the corgis out of the database and just displaying the URLs, let's say, or some data about it, just so we see a bunch of junk on the screen.
JASON: Okay. That would be --
JASON: Use corgis from db. Does that return an array, or just the corgis?
BRENT: No, just returns the corgis.
JASON: Okay. No arguments?
BRENT: No arguments.
JASON: Okay. And then if I want to do, like, can I just drop them in like this and it will be a dumped object?
BRENT: You'll have to use JSON.stringify to do that. Otherwise, it will, I believe --
JASON: Oh, I didn't do all the dependencies.
BRENT: That's my bad. Just Control-C out of that and restart it.
JASON: Done, done, done, and then I can --
JASON: Nice. So in doing the install and reloading, it's, you know, the format is not great, because we didn't style it at all, but I can see that we are getting back our images. Here's the unsplash URL.
BRENT: Yep, so if you hit refresh in the simulator, as well, when you shut down the server and restart it -- which is, unfortunately, necessary to do that.
JASON: Here it comes.
BRENT: We'll get there. Okay.
JASON: All right, here we go. Okay, that's good. We're in business here. So if I want to show these images, is there like a special image tag?
BRENT: Exactly. So from React Native, we're going to add an import for the image component.
JASON: Like this?
BRENT: And now -- so there may be a couple things to think about here. We could just display the images and kind of -- well, maybe let's do that first. Let's display them in a column first. And then we'll switch it over to like a row layout, so it looks more like the photo zapper, that kind of thing. But it's worth pointing out here the default for flex box in React Native is to use flex direction column, which is different than on the web. And that was an intentional choice, because usually on mobile layouts, you want things to flow vertically rather than horizontally. People tend to use their phones in portrait mode. So it was kind of a pragmatic choice. I'm not sure if people would make this choice again in the future. I don't think anyone's particularly upset about it, but it's something that is a little divergent.
BRENT: Interesting difference.
JASON: So do I -- I'm kind of making some educated guesses here. Am I in the right direction here? And then do I still use Source-alt?
BRENT: Source with source, full thing spelled out. And then we have to provide an object. So source with an object. So there will be, like, two curly braces on either side. And then we use the URI key, so URI. And then we pass in the URL here. So corgi.url. And for providing the alt tag, we want to use the accessibility label. So we have to say accessible equals true on it. Sorry, this isn't in the source, it's outside. That's just a prompt.
JASON: Like this?
BRENT: Yep. Then add the accessibility label.
JASON: Accessibility label, and that would be corgi, dot -- what did we call it?
BRENT: I don't think we put a description on it. I think we just put corgi.
JASON: I got it. That's on me. Each child should have a unique key prop. Okay, so I can do that. Key equals corgi.id. What are you unhappy about? What did I do wrong? Son of a biscuit! Oh, it would be corgi like this. Okay. Then I'm going to dump this part.
BRENT: Yep. And then we're going to see something interesting here, which is that we're not going to see anything at all. So the reason this is, is another divergence from the default on the web. If an image is a remote image, and we don't know the dimensions of it up front, then it's assumed to be 00. Reason for that, in mobile apps, you typically don't see the kind of experience of an image appearing and then shifting the layout of the entire app.
JASON: Sure, okay, yeah.
BRENT: It's certainly not ideal on web, but something people have kind of become accustomed to, so not so much of a faux pas. So in this case we have to specifically give it a width and a height.
JASON: Does that go in the source?
BRENT: We can just add it on the root props for the component. So under the style key -- style --
JASON: Then just -- yeah. What did we set these? Were these square?
BRENT: Yes, I believe it was square, and then there was like a resize mode set on it.
JASON: Does it take straight pixels like this, or do I need to give it unit?
BRENT: Actually, in this case, you can treat them like pixels, but they are actually points on the device. And so it will automatically factor in the pixel density.
JASON: Interesting, that's cool.
BRENT: There we go.
JASON: Who's putting not corgis in this corgi app?
BRENT: I'm guilty of that.
JASON: Look at that. That is a good corgi. Okay, this is great. Here we go.
BRENT: That was me.
JASON: You did this? You did this! Okay.
BRENT: Well, if you saw the intro photo that we had in the beginning of the stream, you'll see my dog that looks remarkably similar to some of these dogs on here. There we go, like that one.
JASON: Yeah. They are very good dogs, I will say.
BRENT: So we might want to also just specify a resize mode here. So we've given it 200x200, but maybe we want everything to just fit within -- rather, we want everything to definitely take up 200x200 in the space that we provided. So we can set the resize mode to cover.
JASON: Like that?
JASON: Not like that. There. Okay.
BRENT: That will take care of it for us. So if we have some image that ends up being really -- like much taller than it is wide, or taller than it is wide, then we'll protect it from looking silly in the UI. You can also change it to contain and a couple others. Similar to what's supported on the web.
BRENT: Okay, so we have some corgis. That's pretty good. Pretty good start. So they don't belong at the corgi expo though. Yeah. They are honored guests at the corgi expo.
JASON: Ah! Hold my bucket! Okay.
BRENT: Go over to the iOS app, you'll notice we can't actually scroll in the app.
JASON: Yes, I noticed that. I'm attempting to scroll up and down, it's not working. Pulling up and down.
BRENT: This is another component that's needed here, we need to actually add a scroll view. Default on the web is for the root component, root element, to be scrollable on the vertical axis, so you get that by default. But we actually need to explicitly add a scroll view here. So what we can do is, first, let's look at the style for the wrap view. Scroll down to the bottom. We have a few things that we probably don't want to keep, like line items, center, justify, content, center. We can just delete those.
BRENT: Then we're going to import the scroll view component.
JASON: From React Native?
JASON: And that's -- what it?
BRENT: Yeah, I can't remember what that is.
JASON: What is it when you capitalize the first one? I don't remember.
BRENT: Someone will tell us.
JASON: We'll find out shortly. Is that PascalCase? And this is SnakeCase, and Screaming Snake Case, which is my favorite. Apparently, this is actually the name for it, which I love.
BRENT: I think kebob case has to be my favorite.
JASON: Kebob case --
BRENT: Sounds delicious.
JASON: It really does sound delicious. So I have the scroll view, and then do I just put it around view?
BRENT: Let's put this inside of the view.
BRENT: Child of that, so it will wrap everything else.
JASON: Got it. Including the status bar?
BRENT: That doesn't actually matter where that goes. We could do it. Okay.
JASON: There we go. Look at that, it's already got the bounciness of an iOS app.
BRENT: Exactly. This is just using the native UI scroll view component. No emulation or anything happening. Just exactly as-is in any other iOS app. Cool. So maybe we can answer this quick question from chat, where is the status bar. The status bar just impacts the text that you see at the top of the iOS app, where there's the WiFi indicator and network and so on. And so the component itself doesn't actually render any UI in place, but, instead, it changes the global status bar UI. And so there's another way you can do that by making imperative calls to that, but some people prefer, including myself, to use a component to do it. Then you have different screens. If that component is the deepest active component, it will take precedence and style the style bar appropriately.
JASON: Oh, cool. How does Expo handle the notch?
BRENT: The cut-out things, yeah. So there's this library called React Native Safe Area Context. And maybe we'll skip over including that here, since we're already on iPhone SE and we don't need to switch over and do all that. But I can share, and we can share in the show notes, the example that I --
JASON: What's it called?
BRENT: Safe Area Context. There you go. And this allows you to programmatically read the safe area insets. It's a notch on the top of the screen, home indicator on the bottom. And you can automatically inset that using a safe area view component that it provides, or you can read it programmatically and add it to your padding or do whatever you need to do with that.
JASON: Nice. Cool.
BRENT: Okay. So let's lay this out so it's in a grid, and we can probably just move on from there to -- well, we'll decide when we get there. So to lay this out in a grid, let's go back up to the scroll view component, and we'll just style this in line. You can define styles on style sheet create if you like to. When we're just messing around, doesn't hurt to include style in line in the component itself. So there are actually two style props on scroll views. There's style, which refers to the container of the scrollable area, and then there's the content container style. So we're going to add this to content container style.
JASON: Like that?
BRENT: And I believe that's what it's called. And then we're going to say flex direction row.
BRENT: And probably let's -- maybe actually a better way to do this, because we have the "behold my corgis" text there, we might want to instead wrap the images in a view that uses flex direction row. So we can get rid of the content container style and move that. Yeah.
JASON: Get rid of that all together, and prettier has failed me. Oh, because I didn't finish. There we go. All right. So now we have the text. We've got this, good. And then if we wanted to make these -- this is fine. I think this is good enough. We're going to have to make these wrap, right?
BRENT: Exactly. We'll want to now add flex wrap.
JASON: That's right.
BRENT: Who really remembers these properties anyways? Because the width is already more than half the width of the screen on the iOS simulator, it will just wrap all of them right away. So we can get the dimensions of the screen, and maybe divide it by three. Yeah, that will show you.
JASON: Oh, you're getting the dimensions of the screen? How are you doing that?
BRENT: So we can scroll to the top and import the dimensions API from React Native.
BRENT: Yeah, yep.
BRENT: Bit of an awkward API, but we'll use it here for simplicity. And now to get the width, we do dimensions.get, and pass in the string window.
BRENT: And then get the width property off of that. And then we'll divide that by 3.
JASON: You want this to be square. Perfect. Beauty.
BRENT: Nice. So it might actually be worth here, if you notice in the iOS simulator, we have the text underneath the status bar.
BRENT: So it might actually be worth us going ahead and adding the safe area context library and wrapping, for simplicity sake, the scroll view entirely in the safe area view.
JASON: Sure. I need to stop these, don't I? Yeah, Expo, install okay. So I'm going to start. Good. Then I'm going to open up the iOS and the web.
BRENT: Might have to hit reload still.
JASON: I thought I was doing that. It's so fast that it's not actually showing anything. Let's load it and then we'll see if it's working.
JASON: So then I import --
BRENT: Two things from here. Safe area view and safe area provider.
JASON: React. Okay.
BRENT: Then let's make another component that we export as the root. We'll call it maybe "app container." We can get rid of the current default export, just call it function app.
JASON: Okay. So you want to use the app down here?
BRENT: Yeah, or you can use it before it also. Doesn't really matter.
JASON: Function. Okay.
BRENT: Then we're just going to return the app wrapped in safe area provider. Exactly.
JASON: Like that?
BRENT: I guess you're --
JASON: I have to actually return it.
BRENT: Like the stone age.
JASON: Okay. And then?
BRENT: So now we can change the view that wraps the entire thing to just safe area view. Looks like the iOS app isn't reloading.
JASON: Yeah, I feel like something went weird with it.
BRENT: Try pressing Command-D.
JASON: Command and D?
BRENT: Yeah. Try pressing reload there. Seems like maybe something is going on, where I can see one of the cursors -- try pressing Shift. Okay, I think that might have fixed it.
JASON: Yeah, that seems to have done what we needed, right?
BRENT: Yeah. Just added a little inset, so it's not under the status bar.
JASON: Didn't add it here, which is nice.
BRENT: Yeah, exactly. There's no need for that there. But, of course, we do need it --
BRENT: This would account for the insets from the home indicator in the case of the iOS X and higher devices.
JASON: Cool, very cool.
BRENT: Great. So I think we can go back now, and we kind of have two choices for where to take this. We can follow through and get parity with the previous example by adding sign in, anonymous sign in, potentially sign-up button, as well, if we care about that. Add the text input, where we drop in a link to it on unsplash corgi image. Or we can build a kind of modal, so when you press on one of the corgi images, we see like a larger version of the image.
JASON: So I feel like is there anything inherently different -- well, chat, what do you think? Do you want to see a modal or form inputs? I'm going to give you five seconds to decide and then I'm going to decide for you. Two votes for modal, we're going modals. Make something happen when you boop corgis. Perfect, let's do modals.
BRENT: So when you boop a corgi, you get a modal. Sounds good. Okay, so let's -- wow, overwhelmingly modal.
JASON: Nobody wants forms.
BRENT: Too real. All right. Let's import a couple components from React Native. Let's import a component called touchable opacity. There we go. And another one called modal.
BRENT: So one thing that we need to consider here is that the modal component is actually not implemented on web currently. Web use a work around for this. But we'll just have to factor that in. And, in fact, so that we can actually properly do a nice work around, let's also import the platform API from React Native.
BRENT: Cool. So we have three things we need. Let's start by making the corgis boopable. So we're going to wrap each of the images in a touchable opacity.
JASON: Right, right.
BRENT: Then we can add onPress. It's not called onClick, because you're not really clicking. Press is a more generic word that can apply to click for an actual tap or variety of things like that onPress takes a call back that does something. So for now, let's just write alert. So we can use the same window.alert kind of thing, but just call it alert. We can just say boop, I guess, for now.
BRENT: So we should be able to see as we tap on one of these corgis, same thing should happen on the web.
BRENT: Perfect. All right. Now we can put this modal component to use.
JASON: Yeah, let's do it.
BRENT: So probably what we want to do here is have something like a selected corgi or booped corgi state. And when you boop the corgi, we set it as the booped corgi.
BRENT: When there's a booped corgi, we display the modal.
JASON: Do I do that with regular old hooks, like use state and everything?
JASON: So let's use states, and we will say we've got booped corgi, set booped corgi. Use state, and leave it undefined for now. And then down here, when we do the boop, we want to say set booped corgi to set the corgi.
BRENT: Cool. So now we can render this modal component. Doesn't matter too much where we put it. Let's put it outside of the scroll view as a sibling of the status bar. And modal takes a few properties that we care about here.
JASON: I'm doing it like this, right, I want to not show it if there's no booped corgi?
BRENT: We can still have the modal component rendered if there's no booped corgi.
JASON: Oh, okay.
BRENT: It doesn't matter too much. Let's keep it rendered, yeah, in case we want to do an animation for it.
JASON: Works for me. Oh, stampede. Oh, oh, there we go. Party. All right.
BRENT: So there are a few props we care about. Let's set transparent to true on the modal. And we'll set visible to the -- I guess coerce the Boolean out of the booped corgi.
BRENT: Cool. That will work for now. And now let's add a view here, and we're just going to create like a container to put the corgi image in. And let's set the --
JASON: Do I want the same thing here?
BRENT: Yeah, actually, that will work fine if we copy that in as a child.
JASON: Okay, I'm going to pull this across, and instead of divided by 3, we can make it the full window width.
BRENT: Exactly, yeah. Realistically, we would want to do some logic here about whether the device width is greater than the height, and use the height, otherwise use the width. Let's not worry too much about handling -- we'll assume everything is in a situation where the height is greater than the width, and just use the width. Yep, that's good. We also need to -- yeah, that's great.
JASON: Just making sure we don't try to render if we don't have one set.
BRENT: So now what we'll want to do with the view, is we'll want to make sure we center the corgi. So we'll set some style on the view. We can add back, do flex:1 to take up the screen. This modal component actually just renders at a different layer above everything else in the app. It actually uses the underlying native API for that. So we can just trust this is independent of the hierarchy, so it's going to take up the full screen. Maybe we want to set like a background color on it. We can use RGBA for this and give it a little bit of transparency as a background color. Maybe 000, 0.5, something like that.
BRENT: So let's see what we did wrong.
JASON: Let's see what we did wrong. You knew that. Wait, that one worked.
BRENT: So the modal component doesn't quite work on web yet, but we'll add our work around for that. Cool, we didn't do anything wrong on native, except for the fact we cannot dismissed a booped corgi once it's been booped.
JASON: There we go. Little bit smaller. Perfect. Beautiful.
BRENT: That little design touch.
JASON: Okay, so now we need a dismissal.
BRENT: Yeah, so the easiest way to do this right now is, without digging into other concepts, let's add a touchable opacity as a wrapper to this image again.
JASON: Oh, got it. Touchable opacity, and then drop the other side down here. And we need to onPress, set booped corgi to null?
BRENT: There we go.
JASON: Beautiful. Look at it go. It's amazing, there's just a tiny bit of niceness that comes with that, that just feels polished that I really like. You know, these are the sorts of things, oh, that little bit of transition that happened there, that all would have required just a lot of CSS. So it's nice that just works.
JASON: Okay, so if we want to make this work on the web, what needs to happen next? Touchable opacity is the -- is what makes it clickable. Why is it called touchable opacity, because it fades in and out?
BRENT: Exactly. So it changes the opacity as you change on it. Also a touchable highlight, which will change the background color. Touchable without feedback, which has no physical output on the view. And, I believe, people build things like touchable bounce, which will make it scale a little bit when you press on things.
BRENT: You can make your own effects, as well, with it. Little out of the scope we can cover today in 28 minutes.
JASON: For sure.
BRENT: Yeah, so some animations. We can add animation type as a prop to the modal component first. Then we'll get this working on the web.
JASON: There we go.
BRENT: We can say animation type equals fade.
BRENT: So a little subtle.
JASON: Subtle, might not even be coming through on the stream, but looks really nice.
BRENT: We can also change it to slide, which might look funny, because we have a background that takes up the full screen.
JASON: Whoop! Whoop!
BRENT: Probably in this case you'd want to do something that doesn't involve a full-screen background. Fade is going to look better. Yeah, someone also asked kind of about native animation, something like a shared element transition, where it would go from where you would press on the image to transition the image to the new view.
BRENT: And this isn't something that's built in React Native out of the box, but there's a library called React Native shared element, which does a really fantastic job of this. And so, yeah, feel free to check that out. My coworker Hine built this. It's really awesome.
JASON: We'll throw that in the show notes, as well, if you want to give that a shot.
BRENT: Cool. So let's make it work on web.
JASON: Absolutely. This is -- I'm not going to lie, I'm kind of blown away how quickly we got this done. Okay, so let's do it. Let's make it work on the web.
BRENT: Okay. So to make this work on web, we're going to take advantage of the fact that position fixed will make the element relative to the root element rather than relative to the parent. So on this style prop, on the view wrapper that we have -- so right there. Yep. Let's do dot, dot, dot. Yep, we're going to use that in a moment. So dot, dot, dot, and then platform.select.
JASON: Wait, no. Platform.select.
BRENT: It's a function. It takes an object. And then we're going to use the web key on the object, and then that will also be an object itself, which is a set of styles that will apply only when this is running on the web.
JASON: Oh, I get it. So this -- there was a question earlier about this, and I didn't want to derail us, but I'm glad we got here. So if I want to apply styles to only one platform, like if we're doing something silly on the web, whatever, we want to play with some CSS-specific stuff on the browser, we would put it here.
BRENT: Yeah. This is one good way to do it. Of course, you can play around with however you want to actually make that work for you, but you can pull it out to variable or do whatever you want to do there. But then there's also, as I mentioned earlier, .web.js extension, also iOS.js, Android.js, so if you had a styles file, files.web.js and you pull this in for the import styles and the modal for web, we'll have these styles pit in. Okay, so we have fixed. Now we need to set top zero, left zero, bottom zero. Basically, take up the whole thing, please.
BRENT: Okay. All right. So --
JASON: Uh-oh. Oh, wait. I think our modal is just not -- it's not clickable.
BRENT: Right. So the image isn't visible currently. So let's -- what's going on? Oh, right. Right, because we're rendering the view, regardless of whether we booped a corgi or not. So we might need to move this into the booped corgi.
JASON: Totally fine. Let's do that. Just move this down. Boop!
BRENT: There we go. Looks silly, because we haven't -- maybe this would be a good time to change the styles. Basically, because the width is greater than the height, we're going to end up with the silly looking corgi. All right, yeah, we can -- there we go. Good work around.
JASON: When in doubt, hit it with a hammer. Oh, no! Boom, there we go. So now we have a web app working that will let us show these photos. We have an iOS app running, and, theoretically, I don't know I'm going to also try to fire up an Android emulator same time we're doing Xcode and my streaming stuff, but we have an Android app running back here we can get if we wanted to.
BRENT: Someone on the stream can verify it. If you press G to open up the dev tools again, browser-based web tools, we can click on connection, change to tunnel.
BRENT: Then if someone has the Expo client app on their Android phone, they can scan the QR code. Take a screenshot of it, if you plan on doing this, so we can switch away. Scan that QR code in the Expo client app for Android. It will take a minute to load. It's loading over the tunnel connection. But, eventually, it will load, and you'll see that it will work fine.
JASON: Looks like somebody is doing it. Something is happening in the background. Cool.
BRENT: Looks like Ruben is doing it. Thanks, Ruben.
JASON: It's going to be fine. Don't worry about it. It's just like working on a VPN. No, this is great. How cool is that, that just happened? Here we are, how do I go back, do I just delete this?
BRENT: Yeah, you can close that. Sorry, whoever is debugging.
JASON: Oh, no, what did I just do? Where did my debugger app go? Whatever. I can open this again.
BRENT: Cool, people confirmed that worked.
JASON: Super cool.
BRENT: So let's just deploy the web app to Netlify real quick, so we can see what that looks like.
JASON: Okay, hold up. We're going to go up and get this live on the Internet is what you're saying?
BRENT: Yeah, yeah.
JASON: I love it. I'm so into this. So I'm going to stop this now. All your Android apps are about to break. All right, we're done. So what do I do next?
BRENT: So we need to run the build command to produce a distribution build for web. So to do that, we run Expo build:web.
JASON: I just pushed the button, any parameters or anything?
BRENT: No, we don't need to pass anything. There's some good defaults provided. I think it's probably going to be a pretty large bundle, because Firebase is massive, but we'll see.
JASON: It's okay. We're learning. Using node to generate images. Yeah.
BRENT: What it's doing is generating various icons, PWA icon, things like that, generating different sizes for each asset. So now we have the web build directory, which we can load straight to Netlify.
JASON: Cool. So I'm going to Netlify in it. And -- oh, I need to do GitHub. Cool, we can do that.
BRENT: We could actually just go into the web build directory and run Netlify deploy. That's the easiest way to do this.
JASON: Yeah, I guess we could do that. Let's do that. So we'll do Netlify deploy. Get it out there. Create and configure a new site. On my team, we're going to call this "Corgi Photos Expo." Publish directory is this one. And it's done. Beautiful. That -- I love -- this is a cool workflow. We just built a native app, a web app. IOS, Android, web, all at once, and we've already got the web app online. There would be more to it to get the native apps up, because there's app store reviews and that sort of thing.
BRENT: Totally. So in terms of actually getting the build itself, it would be very similar process to what we just did, where rather than running Expo build web, Android, and we guide you through the process of creating all the credentials that you need, like the key store, profile, and so on. We give you this result, which you can take and upload on the App Store and Play Store, but that process of taking it and putting it on the store, the first time you do it is a bit arduous, I suppose. You have to provide screenshots on a variety of different device sizes, provide a bunch of metadata and so on. Yeah, we could maybe just do a simple mock version of this by running Expo build:iOS ST simulator. Or even leave out the "T" and we'll be prompted.
JASON: Like that?
BRENT: Yes, and this will do a primary build -- got an issue there.
JASON: I'm in the wrong directory is why.
BRENT: Yeah. Okay. So this will take -- you can just use the suggested one. Doesn't really matter.
BRENT: And due to some interesting stuff security related, there's a command we need to run to actually open this app in this simulator, but this is often a good way to say, okay, I want to test this now outside of the Expo client app. I want to test it in an environment that's closer to the production environment. So then you can do a simulator built for it, if you're not already on Test Flight or something. Makes it easier to get something running on your machine. And it's closer to its final form.
JASON: So while we're waiting for this to build, there's a couple questions in the chat that I'm also curious about. So if we were going to put in media queries for the web app to make this a fully web app/desktop experience, would we just use the platforms.web, or how would you approach that?
BRENT: There's a couple libraries for media queries. And so Evan Bacon, who I believe is actually in the chat currently, made one called Expo/Match Media. Yeah, first post that you see in the results is something that Evan wrote about how to do media queries that will work on all platforms. And so this is a pretty good tutorial for that. It uses a hook called use media query from React Responsive. And it poly-fills the APIs that are needed for that to work.
BRENT: Yeah, so that's what I would do.
JASON: Okay. And then another question was about is there anything planned to automate generating required app store screenshots or any of the things you'd need to submit the app for the first time?
BRENT: We definitely are interested in helping more in the submission process, and we kind of think of the issues in the submission process as being grouped in different categories. There's the first-time submission, which is by far the most difficult, so there are a variety of ways we're thinking we can help with that. The nth submission is extremely easy in comparison. So far -- sorry.
JASON: So after you do the first one, sounds like the first one there's paperwork you got to fill out, it sounds like generate a bunch of screenshots. The second one, is the second one as easy as I have generated a new bundle here, or what's involved in that?
BRENT: Then you can go into the developer console or app store connect and decide whether you're going to start distributing through Test Flight or make it a beta, or whatever you need to do at that point. But, yeah, it's close enough to running Netlify, deploy, although, unfortunately, still slowed down by the process required of going to the website afterwards and manually shepherding it along through the release process.
BRENT: Do we want to add automatic screenshots, I think that would be really cool. It's not something that we're working on for the next six months, but perhaps at some point not too far after that.
JASON: Looks like there was a recommendation for Fast Lane. I don't know what that is.
BRENT: Fast Lane is great for that. A lot of the tools we build are built on top of Fast Lane. It's this great Ruby library, actually, that does a lot of these things for you, like helping you do builds with different environments, helping do things like run tests, capture screenshots, manage credentials, submit to the app store, and so on. Yeah, so there's some cool stuff with that. It's a matter of building user experience around this and making it feel really good and really easy to do. Yeah, we'll hopefully get there at some point.
JASON: Nice. It's still building. Is it hung, or does it just take a while?
BRENT: So it's queued right now. Part of the way that we actually make money as a business is that we -- this build service is free, but during peak times, rather than us paying for more machines to handle the peak load without any wait, we currently added a queue. So if you are a free user, you might have to wait at the peak time maybe 20 minutes for a build to go through. So, yeah, it's something where it's just kind of part of how it works. You can also set up turtle CI on your own CI server, if you like, and it will run the builds for you. Looks like we might be queued here. If you can click back to your terminal, you can command click through to the turtle status link. So at the top of the screen. Yep. And this will show us approximately -- this is pretty direct view into what's going on.
BRENT: 137 builds for Android in the last hour.
JASON: It's ramping up, too, it's getting busier.
BRENT: Yeah. Yeah, we're right around the peak time. Maybe I should not have suggested doing build. This might work at some point. We can leave it in the background and see.
JASON: We'll let that keep running. While we're doing that, so, chat, you've got about five-ish minutes left to start asking questions, if you've got any. So drop those now. Brent, for someone who is, you know, watching this and wants to go a little bit deeper, are there any resources that you would recommend somebody starts with, if they want to take this further?
BRENT: Yeah, I think that there are a couple of good official resources. There's React Native.dev, they've put a lot of work into improving the onboarding experience in the docs. If you click through the docs there on the top left, there's some really good introductions that kind of go through things at, I think, an easily digestible pace. It has notes that apply to people coming from a web background, iOS background, Android background, kind of to make sure everyone is able to keep up. And, actually, what you're seeing there embedded in the page if you scroll up a bit is a thing called snack. This is like code sandbox for React Native apps so. It allows you to make some changes to code and then see it live on the page. So there's a lot of interactive examples built into the documentation. I don't think you adjusted as you resized the windows, so you can't see the preview. Maybe toggle the preview off and on, maybe it will show. Actually, I think this website isn't intended to be viewed as this width. If you zoom out further --
JASON: There it is.
BRENT: You can see how the web version of it, but if you click over to my device, that will give you a QR code you can scan.
JASON: Oh, cool. This is like the tunnel thing?
BRENT: Yeah, exactly. And so this will just allow you to connect and make changes right in your browser. Kind of nice interactive examples while learning about the core components in React Native, the fundamentals, the dreaded text input and forms, list views, which we didn't cover here, which is definitely a valuable topic to learn about. We kind of rendered all of our image components as-is, but you probably want to use a component that is better suited for rendering large lists of data once you get beyond a few.
JASON: And the reason for that would be -- are these virtualized lists? It's going to pull things out of memory so you don't bog down your machine?
BRENT: Yeah. So the optimizations that the list views and React Native core use don't actually unmount views as you scroll past them, but it will easily render the views that come on to screens.
JASON: I got you.
BRENT: It will only render ahead a certain amount, and you can customize that. Also provides things like the sticky headers, if you use a thing called section list. So if you scroll, maybe a header will stick to the top of the screen, as you scroll past it, so you know what section you're in.
JASON: Okay, cool, yeah. Okay, so got a couple questions in chat. How does Expo manage -- go ahead, sorry.
BRENT: One other resource is a tutorial I wrote in the Expo doc. So if you go to docs.expo.io, there is the first thing that you land on is get started. And the first steps you should the tutorial link on the side bar. So this just basically guides you through building an app, not too dissimilar from what we're looking at here today, but something where you can actually use a couple of native APIs, like an image picker from your camera or camera roll on your device and take that image and share it with someone else using the share API.
BRENT: Splash screen, icon, and that's another thing I'd recommend going through.
JASON: Nice. Very cool. Okay, questions from the chat. Bruno asks, how does Expo manage push notifications?
BRENT: So we actually, I think, have a pretty awesome way of doing push notifications. We have a push service that abstracts over platform differences in push notifications. We currently don't support web push notifications, but for iOS and Android, you basically as a consumer, you receive an Expo push token. Yeah, we have a very long documentation page about it. But you get a Expo push token, which as the developer, it doesn't matter for you if that's an iOS notification token or Android notification token. You just say I want to send a notification to this expo push token and use one of the server SDKs that Expo provides. I think we have node, Russ, Ruby, and a variety of things a lot of people in the community have built.
BRENT: Just basically say send these notifications with this content to this token. You can send batch notifications. It's relatively built in. If you scroll in on the push notifications overview page, there's an example here. So this example kind of is end-to-end showing you more information than you even need to know. This will allow you to bypass even using the server SDK. This is all the code that you need to send a notification to iOS and Android from even the app itself, which makes no sense. You would not send a push notification from the app. You'd send it from your server as a complete example. And you can copy this into your app.
JASON: Nice. Cool. Okay, so couple more questions here. Can you use Swift UI views as React Native components for iOS?
BRENT: Not at the moment. I think there's potentially a way to wrap that in UI views. I'm not too familiar with how that is planning on working, but I have heard that someone very smart is potentially going to be working on a similar project.
BRENT: Could be something we could see in the near future.
JASON: Let's see, John asks is there a way to sync user interactions between screens when doing development? So if I click on the iOS app, if somebody has the Android dev app open, it would go to whatever view they are looking at?
BRENT: Yeah. Actually, someone who works at Facebook, used to work at Microsoft. He actually built something like this back when he was working on App Center at Microsoft. I'm trying to find this article. Yeah, user interaction sync for React Native. I don't know to what extent anything that he's done here is actively maintained. It's built in 2016.
JASON: Sure, sure, sure.
BRENT: But it's definitely doable and something you could explore, if you were so motivated.
JASON: Okay. Let's see, libraries for animation. Looks like there's Reanimated.
JASON: I got you.
BRENT: Yeah, just looking into that first.
JASON: Cool. Okay, so couple questions I think might be a little -- I don't think that we would be able to talk about how anything was implemented without seeing the thing that was implemented. Type script for React Native, looks like when you Expo and knit a project, there were multiple options for type script starter. So if you want to start with type script, sounds like you can run it immediately with everything already configured.
BRENT: Yeah, totally.
JASON: Man, I'm so sorry. We're out of time.
BRENT: No worries.
JASON: If you have more questions, go tweet at Brent. So let's wrap it up here. Brent, go follow not Brent on Twitter. One more shout-out to our sponsors. Netlify, Fauna, Sanity, and Auth0. Thank you so much for White Coat Captioning for doing the captioning. Brent, thank you for hanging out with us today. I'm really excited. Make sure you go check out the schedule, because we have really fun stuff coming up. Remember, you can add the calendar to your Google Calendar and actually just get a notification of what's going on. Later this week, we have -- who's coming on Thursday? I think it's Ken Wheeler. It is, yeah, Ken Wheeler is going to come on, and we're going to do something weird with the web audio APIs. He hasn't told me what it is. We're going to find out. I imagine it's going to be pretty fun, though, because whenever Ken is involved, it's usually experimental and odd. With that, thank you all so much for hanging out today. We're going to call this one done. Stay tuned, chat, we're going to raid. Thank you, Brent, again. We'll see you next time.