Janic Duplessis joins us to talk about the new iOS 14 feature, App Clips, and how he got them to work in React Native. We also quiz him on react-native-safe-area-context and running a custom version of React Native!
Janic Duplessis joins us to talk about the new iOS 14 feature, App Clips, and how he got them to work in React Native. We also quiz him on react-native-safe-area-context and running a custom version of React Native!
This episode brought to you by Infinite Red! Infinite Red is a premier React Native design and development agency located in the USA. With five years of React Native experience and deep roots in the React Native community (hosts of Chain React and the React Native Newsletter), Infinite Red is the best choice for your next React Native app.
Helpful Links:
Connect With Us!
Jamon Holmgren:
Hey everyone. Welcome to React Native Radio podcast, where we explore React Native together. I'm your host, Jamon Holmgren. I'm joined as always by my three co-hosts. Hey Harris, how's it going today?
Harris Robin Kalash:
Hey Jamon. It's going good. Thanks for asking.
Jamon Holmgren:
Good. Good. Is it getting cold up in Montreal yet?
Harris Robin Kalash:
Yeah, it's actually starting to get pretty chilly here.
Jamon Holmgren:
It started actually this morning, it was kind of frosty around here. What about you Adhithi. How is the Midwest?
Adhithi Ravichandran:
It started to get cold again. Yeah, it definitely feels like fall now, so it's going to start dipping.
Jamon Holmgren:
It feels like it all of a sudden turned a corner. Oh, it was the 21st, now it's the 22nd and it's time to get cold.
Adhithi Ravichandran:
Mm-hmm. Yeah, right around Halloween it's going to be really cold here. I think they're even predicting snow next week here.
Jamon Holmgren:
Oh, really.
Robin Heinze:
Wow.
Jamon Holmgren:
Okay. Well, we're a remote podcast, so I don't think you can call in, that you can't get into a podcast recording.
Adhithi Ravichandran:
No, I'd love to keep joining, so yeah.
Jamon Holmgren:
Great. Robin, how are you doing? You're closer to me, so I assume you're seeing the same weather.
Robin Heinze:
Yes. I'm in the same climate as you and it did frost last night. We're going to have to winterize our hoses.
Jamon Holmgren:
Yeah. Oh, I forgot about that.
Robin Heinze:
Well, consider this as your reminder.
Jamon Holmgren:
I here... I'll be right back. And our awesome guest today is a guy that actually lives not very far from Harris, up in Montreal. Janic Duplessis. Did I say that correctly, Janic?
Janic Duplessis:
Yes, close enough.
Jamon Holmgren:
Can you say it for people so that we get the right pronunciation?
Janic Duplessis:
Sure. With my best French-Canadian accent, it will be Janic Duplessis.
Jamon Holmgren:
Yeah, I really didn't get that, but we'll pretend like I did. Really awesome to have you on today. Janic is the co-founder and lead developer of Th3rdwave. A website and app to find cafes and order coffee. I think it's up mostly in your area right now. It's kind of a start-up?
Janic Duplessis:
Yeah, we're mostly in Montreal and Canada right now.
Jamon Holmgren:
Okay.
Janic Duplessis:
Slowly expanding. It's a local app, so we're going city by city and right now we're well established in Montreal, looking to expand to the rest of Canada and then, eventually, the US.
Jamon Holmgren:
Right?
Adhithi Ravichandran:
Very cool.
Janic Duplessis:
In a city near you soon.
Jamon Holmgren:
Yeah, that's awesome. Harris was telling us before the show that your app is a great way to find great neighborhoods because if there's a Th3rdwave café in the area, that means it's a good neighborhood. Is that right, Harris?
Harris Robin Kalash:
Well, it was more for if you're trying to buy real estate, I don't know how accurate this is, but I would say that if you have a Th3rdwave coffee in the neighborhood and people are buying $5.00 latte than that's a good sign it's going to appreciate. Yeah.
Jamon Holmgren:
Janic is also a core contributor to React Native for over four years now, and the developer of the very popular, although I think people probably don't think about it all that often, React Native Safe Area Context. So, we're going to be talking with him about that, but one of the big things we're going to be talking about is iOS app clips. And that's going to be a fun topic when we get there. But before we do that, Janic, can you tell us a bit about yourself, where you grew up, how did you get into coding, how did you get into open source?
Janic Duplessis:
Yeah sure. I've always lived in Montreal, Canada up there in the north. I was always interested in computer video games and stuff as a kid, so I was like, "I kind of want to do that for work later." I ended up studying computer science and here it's called CEGEP, it's before university. It's this weird thing we have only in Quebec. Then after that, I started working for a company that does software for CEGEP’s. It's kind of funny. I worked for the company that pretty much did all the software, all the email stuff, pretty much all the online class stuff that there is now with COVID. We had some of it there where you could hand in homework and stuff like this. Chat with your teammates. Yeah, that's how I got into app development with my internship there and then I worked there also part time while going to university.
Janic Duplessis:
It was nice. It's funny because I got into building infrastructure for apps, so React Native, right? You use React Native to build apps, but there I was working on their in-house web view-based framework for the mobile app, because the mobile app was just a web view, basically and I was the one that was working with both iOS and Android to add the bridging code to access the Native feature from the web view.
Janic Duplessis:
So you think of Cordova and all these stuff, we had our in-house thing like that, so that's how I got into Native development a little bit also. Because I never did in school, I learned C-Sharp and a little bit of JavaScript so working there I learned JavaScript properly, because it was a JS app. It was this place where everything was done in-house, so they had their own JS framework, they had their own... We wouldn't use React or anything, right? It was just everything was written. There was almost no dependencies or whatever.
Jamon Holmgren:
There's some value in doing that sometimes, even though usually it's not especially efficient.
Janic Duplessis:
Yeah, I mean, for me it was nice because I got to work on this kind of lower level tooling stuff, but starting from use React. It was interesting for me, at least, out from school and I was just starting off my career, I guess. So yeah, I got to learn JavaScript properly, also a little bit of Native for objective C on IOS and also Java and Android. So, actually, that was a really, really good foundation for working on React Native later.
Jamon Holmgren:
Yeah, totally. Yeah, it's a very similar problem. So, if you were kind of doing a lot of your own stuff and not really using open source as much. How did you go from that to being an open source contributor?
Janic Duplessis:
Yeah, basically, after two years I started a consulting company with one of my friends. App & Flow actually still exists. I think you know Charles, right? Charles Vinet from App & Flow? I know Harris knows him.
Harris Robin Kalash:
Yeah. I know him.
Jamon Holmgren:
Yeah, actually, we just chatted in July. We have a shared channel with App & Flow. I didn't actually realize that you were one of the co-founders of App & Flow. That's very cool.
Janic Duplessis:
He still runs the agency right now, so I started doing that. Basically when we got our first contract, we both quit university and our jobs and we were like, "Let's do this. We're going to do our own thing and do apps." So, that's pretty much when... Actually, I started contributing to React Native a little bit before. I think I just saw an article about it coming out when it was released and I wanted to try it. Actually, I remember, yeah, when I was in university, it had the worst web services and whatever. The email client was really bad, everything was really bad. So, I built an app with React Native to access my emails and my grades.
Adhithi Ravichandran:
Wow.
Robin Heinze:
Wow.
Janic Duplessis:
So, it was just a little personal project for fun, so when I saw React Native coming out, I was like, "Oh, that'd be a cool project to do." When I was in school-
Robin Heinze:
Do you remember what version of React Native?
Janic Duplessis:
Well, that was the first, pretty much. Well, I don't remember the actual number, but it was like early when it came out. 2015, I think. I don't even know if there was Android support yet. Maybe it was when Android came out. I'm not sure, but it was just released and actually, that's when I made my first commit to React Native because there was a random... I think there was a crash, but it would crash the whole app instead of popping up the red screen. So, I fixed the Native crash. When it would crash in Native, it was to just show the actual error.
Robin Heinze:
I'm guessing there was a lot of low hanging fruit back then.
Janic Duplessis:
Yeah, I mean, to be honest, it was so easy. If you wanted to contribute, it was the best time to get into it.
Robin Heinze:
Yeah, I'd say much easier than it is now where everything's pretty solid.
Janic Duplessis:
Yeah, right now it's... There's still some stuff, especially with the new rewrite the dark coming out. But yeah, back then it was so new that pretty much, you would find a crash just by using it and... I don't know, I always like... If I see something that crashes in my dependencies or whatever, I'm not going to go file an issue in GitHub, I'm going to try to find what the crash is and fix it. I'm just submitting a pull request right away. That's always how I work and how I pretty much did most of my work on React Native. I would just use it for my own products or projects, and then if I find an issue or I found a feature that's missing. Sometimes it's just there's a new thing. Actually, I noticed something recently. There's a new prop for the date picker on IOS, there's two styles now, but there's no prop to control it right now. So, right now, if you want to make a PR to React Native, go add the prop to control the style of the date picker and everyone's going to win.
Adhithi Ravichandran:
So, it all started with your university website being so bad. Thanks to them, you got into React Native.
Janic Duplessis:
Yeah, that can be it. I don't know if I would have got in it anyways, but yeah. It was definitely a cool... I always, especially back then when I was still in school and stuff, finding little projects to do on the side. That's kind of how I got into open source and using new stuff.
Jamon Holmgren:
Okay, we're going to move into our main topic. Before I do that, I'd like to mention that this episode is sponsored by Infinite Red, as always, the app and web consulting company that I am one of the founders of. We do React and React Native work and of course, if you're considering doing a React or React Native project, hit us up. You can email me directly, jamon@infinite.red or just hit our main website, you can find our contact form there and Missy, who is our sales coordinator will respond to you. I'll see the email as well. We're not that big of a company. You don't have to go through channels to find me. You can find us online at infinite.red.
Jamon Holmgren:
So, our main topic today is iOS app clips in React Native, and Janic actually has a very good perspective on this because he's done some work around that, but before we get into the stuff that you've been doing, what exactly is an App Clip? Because it's not something... I'm an iOS user and I don't exactly even know what an App Clip is.
Janic Duplessis:
Yeah, so it's really new. It actually got released with iOS 14, so it's normal that probably most people haven't used any. I haven't either, I just saw the announcement at the latest Apple conference, their dev conference and I got really excited because their use case is really good for the kind of stuff that we do at Th3rdwave. They had examples with Yelp, which is kind of similar in what we do. What we do is we have a digital loyalty card program for independent coffee shops. They could just have this NFC chip or a QR code, then you can scan it and it will take you to the app. It won't even download the app, it will just download a lightweight version of the app and then it's going to be more similar to a website, pretty much.
Janic Duplessis:
It will just download it right away, open it to their loyalty card page and then you can claim your stand and start using the app without having the full app installed and they kind of upsell you to get the full app eventually, if you like it. So it's like an in-between, between a Native app and a website. There's also the same thing on Android. I don't remember the actual name-
Harris Robin Kalash:
Instant Apps.
Janic Duplessis:
Yeah, I think it's something like this. I haven't investigated that yet, but it's probably the next phase. I wanted to try it App Clips, because usually when Apple does stuff, it actually gets traction. Sadly, sometimes with Android, they do these features and then nobody uses them. They've had Instant Apps for a while, but I haven't seen much use. We were trying to roll out an Apple experience soon.
Harris Robin Kalash:
I actually had a question. The way you described it where if someone wants to use their loyalty card right away without downloading the app. That sounds really cool, but what about authentication? How do you know who they are?
Janic Duplessis:
Yeah, it's basically a full app, so it supports all the authentication methods that are normally available. Apple tries to push sign-in with Apple, of course. They're like, "Oh, it works with sign in with Apple," so people could just tap and create their account using Apple sign in. Or you could support your own... You could create an account through the app, so yeah. Basically you would tap, get your card, you will either sign in through any of our Facebook, Apple or create your own account.
Harris Robin Kalash:
Gotcha. Okay, so you maintain your regular auth flow that you already had?
Janic Duplessis:
Yeah, exactly.
Adhithi Ravichandran:
I think I didn't get the part. Are you saying, now this is integrated with React Native? Do we have App Clip support there?
Janic Duplessis:
It's not integrated 100%. App Clips are actually just regular iOS apps, but they have restrictions on size. So, maybe I could get more into details about the stuff I had to do to make it work better.
Adhithi Ravichandran:
Okay.
Janic Duplessis:
Basically it supports, it's just a regular UIkit app, so you have your app delegate, it's just a regular React Native project would work to be an App Clip, but you want to make some additional optimizations, usually to make sure it stays small. It's also part of another app, so even your project, you will have your app target and you will have your App Clip target.
Robin Heinze:
Okay, so it's like a watch OS app or TVOS or something.
Adhithi Ravichandran:
Interesting.
Jamon Holmgren:
And you recently tweeted about this, I think it was... Well, it was a few weeks ago, but you tweeted, "Working on an App Clip with React Native. Going pretty good so far. Main challenge is refactoring code to allow missing Native modules on the Clip." And then you go on to describe how you worked around that technical limitation. So I assume that it would just crash if it found something that you had optimized out of there or something along those lines?
Janic Duplessis:
Yeah, so basically in your App Clip target, you usually don't want to have all your Native dependencies because you're not... Let's say in the main app I have Stripe for payment processing and in that clip, I don't want to use Stripe because I don't need it. I don't want to include the binary for that because you really want to be careful with your app size. So, every Native dependencies that I wasn't using from the main app, I wouldn't include them in the App Clip. A lot of open source libraries had issues with that because they would try to access constants and stuff, right away, even if the code wasn't... Even if you wouldn't call methods on the module. There would still load some code and then crash because it was missing the Native module.
Janic Duplessis:
So, I had to make sure that, basically the library would never be loaded unless I was in the App Clip. So what I did, is for the App Clip, you will have two different entry points. You have your index.JS for your main app and then you have your index App Clip.JS for the App Clip. So in that JS file, I would inject all the dependencies. I kind of implemented some dependency injection where I would never require the actual Native module from the code. It would just try to get... It would have a function that's like get Stripe, and I would call that, then in the index for the main app, I would inject Stripe and in the App Clip I wouldn't.
Jamon Holmgren:
Okay.
Janic Duplessis:
Then I could also check if it was available or not and then always use that.
Jamon Holmgren:
I see, so your index was sort of loading all of these third party dependencies and then passing it along to any of the sub-files and sub-folders that you were using throughout the app then.
Janic Duplessis:
Yeah, that would be basically what it did.
Jamon Holmgren:
Dependency injection. Very cool.
Janic Duplessis:
Yeah, it was one of the first time where I'm like, "Oh, that's kind of this thing you can do that's useful." I feel like sometimes people, they will start with this huge architecture. They'd be like, "Oh yeah, I have dependency injection, all these things." I remember the first time hearing about it was in Angular where they're pretty big about this thing.
Harris Robin Kalash:
That's a pretty good tweet storm that you posted. I really like it. We should probably include it in the show notes.
Adhithi Ravichandran:
So, with the App Clips, it's kind of a lightweight version of the app, right? I was wondering, let's say if you have a... Your app needs to be purchased from the App Store, or there's a subscription model, how do you handle that when a user just gets the App Clip version of it? How do they purchase the app, I guess?
Janic Duplessis:
Oh, that's an interesting question. To be honest, I don't really know. I don't have a purchasable app or whatever, but there's flow upgrade from the App Clip to the regular app, so I assume you can get the App Clip for free, and then when you want to get the full app, you get it from the store like you would normally. I think in the App Clip there's a link and then you'll see, get the full app and it will take you to the App Store and then if the app is not free, I assume that's where you would actually pay for the full app.
Adhithi Ravichandran:
That makes sense. Kind of like a preview version is available for free.
Janic Duplessis:
Mm-hmm.
Adhithi Ravichandran:
Okay, cool.
Jamon Holmgren:
Do App Clips show up as icons on your home screen?
Janic Duplessis:
They don't. They have a spot in the settings where you can... I think you can access all the App Clips. I'm not sure. I know you can delete them there, because that's what I do when I want to reset my App Clips. They have some spots in the settings, but they're not like an actual app and they don't have an icon on the start... They're usually started by an experience, so by a URL.
Jamon Holmgren:
Oh, I see.
Janic Duplessis:
So either scanning a QR code or an NFC, I actually got NFC tags recently, it's pretty cool. It works really well with, I think it's only though if you have an iPhone XS or later. It just scans in the background, so you don't even have to open your scanning app or anything. You just have to tap your phone near a chip when it's unlocked and it just works. It opens the App Clip. It's really, really a cool experience.
Harris Robin Kalash:
So is that the experience you're going for with Th3rdwave? When I go to a coffee shop, there's going to be an NFC terminal somewhere and I'll have my loyalty card open up automatically?
Janic Duplessis:
Yeah, that's like the optimal feel that we want to go for right now. It's not supported. That would only work on iOS 14 and if you have an iPhone XS or later. This flow would work otherwise for older phones, you have to go in your control center or something and then you click on scan tag and then you can scan it. That's on iOS 14, if you're before iOS 14, it's really bad. I'm not sure what you have to do to scan an NFC chip there.
Harris Robin Kalash:
Gotcha.
Jamon Holmgren:
I have an iPhone 10 and this is the first time that I'm hearing that my phone is too old to do somethings.
Robin Heinze:
I know, right? It used to be the cutting edge of phones and now it's old.
Jamon Holmgren:
So, App Clips, are they not full screen apps? Are they just kind of functional?
Janic Duplessis:
Yeah, they're full screen apps.
Jamon Holmgren:
They are full screen-
Janic Duplessis:
What I showed you was just the preview, actually. It opens the App Clip preview and then you click... If the App Clip actually works, I don't know why it's not working right now, but you will see an open button and then you can click and it opens the full app.
Robin Heinze:
Jamon stole my question again, but I was going to ask about how the visual layout of App Clips and what are you constrained to? A fixed height? What kind of challenges did that present in laying things out?
Janic Duplessis:
Actually, when you open the App Clip, it's just a regular full screen app. So there's no restrictions or anything. The screen that you saw when you scanned the NFC tag is not customizable at all, actually. All you can send is when you create your App Clip, you can set up experiences on App Store Connect and then you can give a URL and you can set an image, the title, subtitle, and I think the text on the button. There's a few things you can customize, it's actually really bad because you have to customize every URL manually. I don't know if there's an API for it, I haven't really did research on that, but right now, for each URL that I have, that I want an App Clip on, I have to go into the App Store config thing and then you can set up the image and all the configuration.
Harris Robin Kalash:
Oh, wow.
Robin Heinze:
Oh, wow, yeah. So it's not a totally customizable URL, like a full app?
Janic Duplessis:
The card that pops when you scan is not, but the rest is. Yeah.
Adhithi Ravichandran:
I was going to ask about the navigation within an App Clip. Is there a restriction on the number of screens you can have, because it's lightweight, how does that work?
Janic Duplessis:
Yeah, so basically, with App Clips, you want a single feature of your app. You won't have a tab bar with all the different sections of your app. It will be more like a single flow. For us, we're using it for the loyalty card, so it will be like I'll get a stamp at the coffee shop and eventually, maybe redeem your free coffee. So will be like those two flows.
Adhithi Ravichandran:
Okay.
Janic Duplessis:
In my case, I pretty much took the full app that I have and I redid the navigation to remove the tab bar and only include the screens that I'm using. So, it's actually pretty easy to do with React Native because you'll have... I can reuse all my JavaScript code, it's the same project, but the only thing is that I have two index files and then they require different navigation configurations, I guess. And then, the one for the App Clips only includes the screens that I need.
Adhithi Ravichandran:
Okay.
Janic Duplessis:
But at the same time, I can reuse the same screens that I use in the app, so the flow for getting a stamp in the app is the same as the one that's used in the App Clips, so it's the same code. You can do some small tweaks. What I do is I set a global variable like, is App Clip at the start of my App Clips, so then I can check anywhere. Let's say I don't want to show a button or something in the App Clip because this feature is not supported, I can check this global variable and just skip this code.
Robin Heinze:
Yeah, that was going to be my next question is how much code can you reuse? But it sounds like a lot.
Janic Duplessis:
Yeah, I haven't customized the flow that much, but I ended up reusing most of the code except navigation where I made a specific navigation stack, or whatever, for the App Clip, but for the actual screens, the UI is basically the same in both, so I can reuse all this code and all the stuff for network requests, all the login infrastructure and everything is the same. So, I was able to reuse most of it.
Adhithi Ravichandran:
It almost sounds like a stripped down version of the original app.
Janic Duplessis:
Yeah, exactly. That was actually the challenge because Apple recommends... I don't know if it's a hard limit or not. I haven't tried to publish an app that's bigger than the limit, but they say they recommend that it stays under 10 megabytes. For a Native app, that's kind of a challenge. The initial version that I made, I think it was 12 megabytes or something. So, what I ended up doing is I removed as much Native dependencies that I could... One Native dependency that I noticed that was really big was the Facebook SDK. So, I ended up removing it and using just... I think it's a library called In-App Browser or something to do a web-based web flow for Facebook plugin instead.
Janic Duplessis:
The Facebook SDK would include a big 200 kilobytes for strings for all the different languages for their share with Facebook buttons. To add a bunch of stuff, it wasn't really necessary just for the Facebook plugin. Also, they include all the share stuff. SDK is actually pretty big, so that saved at least one megabyte.
Janic Duplessis:
Actually, there's another project that I worked on that's really cool. I noticed the way assets are bundled currently with React Native. They don't take advantage of a thing called App Thinning where the app is recompiled when you upload it to the store for each device. So, let's say you have an iPhone X which has two times the pixel density, or whatever. So, it will only include the right assets for this specific device, instead of including all the assets for all the different types of devices. But the way React Native bundles assets currently, it doesn't support this because they're just copied in the app, pretty much.
Janic Duplessis:
If you're doing regular iOS development, you'll use asset catalogs where you'll add your assets in a format and you tell X code, this is times one, times two, times three assets and then it adds some meta data to it to know which assets are what. And it actually, when you compile your app, it will compile all the assets together into a single file, a single binary file that compresses really well.
Janic Duplessis:
So, there's a bunch of optimizations, actually, that iOS does. Not iOS, but X Code does that React Native apps didn't really get the benefits of. So, I worked on using assets catalogs instead of just copying the files directly in the bundle. That's at PR right now. I'm deploying this to production soon. So far it works really well, and that helps save with the space too.
Jamon Holmgren:
That's at PR to React Native Core?
Janic Duplessis:
Yeah, it's a PR to React Native Core and the CLI also. It might take a while to merge because these kind of stuff is a bit hard to land sometimes. It's a pretty substantial change and also Facebook has a lot of internal stuff that you don't know about. So, sometimes it breaks their setup, so I need to find someone at Facebook pretty much to champion the project. If you're interested in trying it out, at Infinite Red or something let me know. That would be interesting.
Robin Heinze:
That's interesting. I didn't actually know that that didn't work. We've been using one X, two X, three X assets and I didn't realize that that wasn't actually doing anything.
Janic Duplessis:
Well, we'll use the right asset, but they will all be included in the final app. So you'd be sending more data, but the app actually uses the right asset, at least. It's just that you're sending more data to the user when they download the app versus what they need.
Robin Heinze:
That makes sense.
Janic Duplessis:
Another thing I noticed, it was a pretty good performance improvement because what happens is since X Code compiles all the asset into a single file, you'll have a lot less disc IO for reading files, since you will only load one big asset catalog thing. I know this. I did a little perf benchmark for fun. It wasn't really scientific or anything, but I could see a lot of times, the first an image would load with the old system, it would hit the disc so it would take very much 10 times the normal time for loading from memory. It's a really small number. It's not even a millisecond, but still. If you have a large amount of different images, it could save a few milliseconds there and there.
Jamon Holmgren:
So, it's interesting because these App Clips may actually, because of the limitations on app size, it might help push people toward optimizing the size of even just regular apps in React Native like this PR. So this is really exciting to hear. When this lands, would we have to... Once we upgrade to whatever version includes your change, would we have to put our assets somewhere else? Would we have to treat them differently or does it just take them and do the right thing?
Janic Duplessis:
The goal is to be as smooth as possible to migrate, so it doesn't require really anything. You'll have to upgrade your project slightly, but it's all involved automatically at build phase. It will add the assets to the catalog and everything, so you just have to update your project. So basically, the normal thing you would do when you upgrade React Native, right? We basically just have to add a new asset catalog file to the project and then when you build, it will add the images to the catalog automatically, so you don't have to do anything. You don't have to change your code or have a different process for managing images. It's really just a small update to the project structure.
Robin Heinze:
That's really cool. I'm excited for that.
Jamon Holmgren:
I'll link to the PR in our show notes as well so people can take a look at it.
Adhithi Ravichandran:
Is this something we can do for Expo apps as well or is this just React Native CLI?
Janic Duplessis:
I implemented it for the React Native CLI, so right now it won't work with Expo apps, but if Expo wants to do the same thing with their bundling process, they have their own way of including assets, they can. I mean, I know the people at Expo really well, so I actually talk with one of their engineer to make sure it wouldn't affect over-the-air updates. Because it was one of my concerns. I was like, "If I change the way assets are bundled, will it affect how over-the-air updates work?" But for Expo updates, it wouldn't because they don't change the original assets at all. They just download the new assets in a separate location and they load that if it exists. Otherwise, they fall back on loading the asset that has been bundled with the app.
Janic Duplessis:
So, for that it should work still. If they make the changes, the changes aren't that big so it's just a matter of copying the assets inside of the asset catalog instead of copying it in the final app.
Adhithi Ravichandran:
Okay.
Harris Robin Kalash:
It's really interesting. I also had a question around, not so much around App Clip, but more around React Native Safe Area Context. I wanted to know what motivated you to create it? What was wrong with the previous solution and the way of doing things?
Janic Duplessis:
Okay, so basically I created this library for Brent from Expo because he needed it for React Navigation. First, maybe I can explain a little bit what the library does, but basically, safe areas are the areas on your phone that are not safe to display content in, so notches, navigation bar, status bar, all this stuff. So, the goal is to know the dimension of those things, especially now with all the different types of phones, rounded corners, notches. Those things didn't really exist before. It was all fun when we had all rectangle screens, but it's not the case anymore. So, now we have to think about this.
Janic Duplessis:
Basically, React Native shipped a view called Safe Area View for a while, but it was iOS only. So, what I did, at least for React Navigation, before they would hard code values for the status bar height and all this stuff, but it wasn't really reliable now that Android also had devices with notches. So yeah, it was basically to solve this. First have support for Android to be able to remove all these hard coded height from React Navigation for the status bar. When you have the navigation bar at the top of your app, React Navigation has to do this UI and you have to make sure that you have the right spacing at the top of the navigation bar. It was a demonstration, yeah.
Harris Robin Kalash:
So both platforms give you the Native insets? They both require Native modules for iOS and Android?
Janic Duplessis:
Yeah.
Harris Robin Kalash:
Okay.
Janic Duplessis:
Both iOS and Android have a Native API for those. iOS has it since they introduced the iPhone X, which was the first phone with the notch. I think it was like iOS 11 or something, maybe?
Harris Robin Kalash:
Yeah.
Janic Duplessis:
So there's that, and then on Android, they have an API. They have two or three different APIs, actually. For Android, it's a little bit more complex, I guess, the implementation. They added notches later. They had an API before for... I don't remember how it's called, but they have an API for that and they also added a different API to get the size of notches. We actually don't even use it because the first API that they introduced also supported notches, so it's fine. We don't need to use both. But yeah, the code is... On Android, it's really hard.
Janic Duplessis:
I often get issues about like, "I have this weird phone and it doesn't work in some cases." I'm having a little bit of a struggle with those. It's not that bad, but still. Even though it's better, there's still some issues on certain phones and sometimes there's also different modes on Android. You have the full screen mode or something, the immersive mode. There are a million modes that I've never heard of. I also got an issue about Android Auto recently, which I had never used before. I didn't even know it existed. Someone was trying to use my library on a car.
Harris Robin Kalash:
That's cool.
Robin Heinze:
Wow. Was it Tesla?
Janic Duplessis:
No, no. Well, Android actually has their own car thing. It's like Android Car... I don't remember what it's called, but they have their own car framework, but the library wouldn't work there, I think.
Jamon Holmgren:
That's really interesting. And, it actually gives you a view that you can use... It's just called Safe Area View. It gives safe area insets applied as padding or margin around the outside.
Janic Duplessis:
Yeah, so basically there's two APIs. It's actually a lot more flexible also than the previous solution in React Native. React Native only included Safe Area View, which always applied the paddings. The library actually started off as... That's why the name, Safe Area Context. It actually started off as a context provider to get the insets in JS with the Hook, I think. Well no, it was before Hook. It probably had a provider API or something like this before. But now we support Hooks, basically you will add a provider at the top of your app. There will be a view that covers the full screen, and then it will tell you when you use the Hook in other components. It will tell you the insets for this view, so the part that overlaps the places where you shouldn't have content, right? Like the notch. If the notch is overlapping the view, it will tell you the top overlaps for 20 pixels or something.
Robin Heinze:
Cool.
Janic Duplessis:
What's cool is that you can also have multiple providers. Let's say you have multiple screens... One use case that's really nice is with the new modal style on iOS, I think it's iOS 13 or something? Where it doesn't cover the full screen, right? Now the modals on iOS... So, you want to have one provider for each screen because if you're inside a modal, then it won't cover the notch, so you shouldn't add spacing for the offset. So the library handles these cases pretty well, or you can just use the Safe Area View which handles everything. It will apply padding automatically or the Hook if you need the values in JS. It will return you the top, bottom left or whatever and then you can do some calculation.
Janic Duplessis:
For some reason, sometimes I like to do, I guess a max. On a device that doesn't have a notch, I want eight pixels of padding, but if it has a notch, I want to go all the way to the notch size. So, I'll do a max between eight and the notch size. If there's no notch, then it will add a small padding and if there's a notch, it will use the notch padding. So, you can add some logic in JS also if you're using the Hook API, which is nice.
Robin Heinze:
Cool.
Jamon Holmgren:
So this has a Native component for both iOS and Android. Is that because there are actual Native APIs that will tell you all this information?
Janic Duplessis:
Yeah, exactly. Basically the Native APIs on iOS, there's a prop on views that will tell you the safe area insets for this particular view. So, depending on where the view is on the screen, it will tell you if it covers the notch stuff. And on Android, there's also a similar API where you can get the insets. And, we also have to support older SDKs where we kind of emulate the safe area with other APIs. So, there's actually quite a bit of Native code that we're using. There's no hard coded value, really. We don't assume that the device has a status bar of 20 pixels or something. We really get the actual value from the Native APIs.
Robin Heinze:
Yeah.
Harris Robin Kalash:
Yeah, I have noticed a huge... Well, actually I should say, I've noticed that dealing with safe area has been less of a pain for me when I used your library versus before.
Robin Heinze:
Yeah, totally agree. The original Safe Area View was very... It's sort of locked you in and the behavior was unpredictable and felt like you really couldn't control what it was doing. Safe Area Context has been a much easier experience.
Jamon Holmgren:
I started doing iOS development with iOS... I don't even remember. I do know though that there was only one type of iPhone screen and it had a fixed size. So you would literally say, "Okay, my app is 320 points wide and I need 44 points at the top for my navigation header, because, you know, why would that ever change? And then the iPhone 5 came out and they adjusted the height. They didn't adjust the width, they just adjusted the height and it was like, "Oh, no. Now have to actually pay attention to the height of this app."
Jamon Holmgren:
But I had dealt with browsers before with different sizes and things like that, so this felt like it was super... building apps on easy mode because if you just always had one specific... And nobody ever built for Android, so you just build iOS apps for a very specific size. Just different times now.
Adhithi Ravichandran:
Well, people are using it in cars now.
Jamon Holmgren:
Yeah.
Harris Robin Kalash:
Yeah, that's cool.
Adhithi Ravichandran:
So I noticed that while I was looking at your Th3rdwave GitHub, I noticed you also sorted the React Native keyboard aware scroll view? I actually used that recently. I really want to thank you for that, because I had a lot of issues with the keyboard scrolling. We had a whole bunch of text input inside of scroll view, and it just was driving me nuts and I found this library. Do you still support this of are you kind of done with it?
Janic Duplessis:
I think it's just a fork, actually. Oops. I don't want to take credit for it.
Adhithi Ravichandran:
Okay. Wait, I see a commit from you.
Janic Duplessis:
I probably committed something. I usually fork a lot of libraries and I do small changes.
Adhithi Ravichandran:
Okay.
Janic Duplessis:
And probably fixed something. Usually I try to upstream my changes, but maybe in this case I didn't. I don't know. Maybe I just left it as-
Jamon Holmgren:
It looks like it came from-
Janic Duplessis:
I have a lot of forks.
Jamon Holmgren:
... It looks like a company out of Spain.
Robin Heinze:
APSL?
Adhithi Ravichandran:
All right, thank you APSL. And thanks for those commits too. It's definitely a big headache dealing with the scroll view issues.
Jamon Holmgren:
There's all these little utilities and people kind of working in obscurity making them. There's not a lot of... I mean, I maintain the React Native web view and that's one of the bigger third-party systems out there. It is not a particularly fun thing to work on. It's like its own whole thing. It's way more work than all of our open source combined.
Adhithi Ravichandran:
Wow.
Jamon Holmgren:
Just web view by itself, and it's also the thing that people pretty much only notice when it annoys them.
Adhithi Ravichandran:
Yeah.
Robin Heinze:
What do you mean? People aren't rallying issues saying how amazing it is?
Jamon Holmgren:
Not too many, no.
Janic Duplessis:
I've gotten one of those, at least. It's cool.
Jamon Holmgren:
It's cool.
Janic Duplessis:
Why not? It's actually kind of the same way Safe Area now. It was a fun thing to develop at the start, but now it's in maintenance mode more. So, I feel like I'm spending most of my time answering issues, trying to find some deep weird bug on weird devices. I mean, it's different work, definitely when you're developing a new library and getting started versus maintaining it and making sure you're not breaking things for other people while trying to fix the problems that some other people have.
Jamon Holmgren:
I have a philosophy around that, Janic. I've been doing open source for a long time, like you, and my philosophy is, "I'm going to make it work really well for my use case and then I'm going to try to review pull requests in a timely manner for other people's use cases. I am not going to investigate their problems for free. They can pay me to do that, that's fine, but I'm not going to do it for free." So, that's been a pretty freeing thing for me, because it's like, "I built this for this use case and it's not working for this other use case. Awesome, it's open source. Please create a pull request, I'll review it."
Jamon Holmgren:
I am volunteering some of my time to help, but it's more in terms of reviewing it, testing it, making sure it works before I merge it back in. I'm not going to really deep dive in and try to figure it out. That took a big load off because when I felt like I had to solve other people's problems all the time, you're just working for free a lot and it just becomes a recipe for burnout.
Harris Robin Kalash:
Yeah.
Robin Heinze:
I think that's one of the hardest parts of our current open-source culture. There's not quite enough contributing and there's a lot of just using and demanding perfection.
Jamon Holmgren:
Yes.
Janic Duplessis:
Yeah, I always noticed, I feel like a lot of people are scared of their nod modules folder. It's a thing I've always been curious about. I don't know, but I feel like sometimes people have a problem in their dependencies and they're like, "Oh, I can't touch this. Go open an issue in GitHub." Sometimes it's really not that bad to just go deep into your node modules, just check, log something, just have a quick look at the code. Sometimes the problem isn't super hard to figure out. You can just add logs in your node module. It's what's great about JavaScript versus other compile languages. You have the source right there, so you can check, you can open your project in X-Code or something, add the breakpoint, try to figure out.
Janic Duplessis:
At least try a little bit, I'm not saying that it's always easy, sometimes it's really, really hard, but I feel like just don't be afraid of your node modules or your dependencies. It's actually just people like you and me and everyone that contributes these libraries. For me, at least, it was always something that I liked and it really helped me become a better engineer, I guess. Not being afraid to dive deep into the issues that are not necessarily my code, but other people's code.
Jamon Holmgren:
We've talked about patch-package before on this podcast, but I think this is a good shout out for that. You can play around in your node modules folder, but then if you want to persist those changes, if maybe they haven't been merged upstream or something, you can just create a patch and apply it with patch-package and then it applies it anytime you run Yarn, which is very cool.
Jamon Holmgren:
I totally agree, Janic. I actually did it a couple of days ago. I dove into my node modules, found an issue that was causing some pain for me. Edited just right in the source there and just tested it to make sure it worked and then submitted a PR upstream. Now, the offending code happened to be mine upstream, but in this case it was kind of a similar thing.
Harris Robin Kalash:
There's something I want to ask. Is there something that you know about in React Native Core maybe that is less known by the public that you think is worth mentioning? Whether it's a component or... Because I know that you've made me discover a few things that were not really documented in the past.
Robin Heinze:
What were some of those things, Harris?
Harris Robin Kalash:
You showed me the... Not pressable, it's bouncable or something? It was like this...
Janic Duplessis:
Oh yeah, the touchable bounce.
Harris Robin Kalash:
Touchable bounce.
Adhithi Ravichandran:
I've never heard of that.
Janic Duplessis:
It's not even exported from React Native. It's just there, so you have to import it from the full path inside React Native. It works well, it's a-
Harris Robin Kalash:
I used it.
Janic Duplessis:
Yeah, it's a cool component, but you really have to know the React Native source.
Jamon Holmgren:
What does it do?
Janic Duplessis:
Well, you know when you press a button and it bounces? It's this feedback for touchable. It's like a touchable with this bounce feedback. Spring type.
Jamon Holmgren:
Oh, okay.
Harris Robin Kalash:
Yeah, it's actually quite nice. It's there, but it's not documented and Janic told me about it and I literally used it for Uplet, my previous start-up. And, that's still the code that's there is using this touchable bounce component.
Jamon Holmgren:
I'm going to publish a library that says that it has React Native button bounce. And then all it does is reach in and reexports.
Janic Duplessis:
There might be one already that does that, I don't know. Or someone just copied the code, because if you want to be safe to make sure Facebook doesn't break it because they decide to change where the file is. Yeah, I don't mind using these internal API because that way I keep my React Native pretty up-to-date and I don't mind playing around with this stuff. It's one of those, there's this. I don't know if there's other stuff that I can think of.
Janic Duplessis:
There's one little unknown module that I actually worked on. It's called Dev Menu where you can add buttons, custom buttons from JS in the Dev Menu. So let's say my app has a secret dev screen where you have some information about which API I'm using. I also added code to navigate to any screen, pretty much like... I guess you'd have this with Reactotron, right? It's called?
Jamon Holmgren:
Mm-hmm.
Janic Duplessis:
Yeah, so it's a bit like a mini Reactotron inside my app that I built for fun.
Jamon Holmgren:
That's fun.
Janic Duplessis:
When you open the Dev Menu from React Native, I added a button there where it's dev screen or whatever. So there's an API for that. I think it's called Dev Menu or something like this.
Robin Heinze:
That's cool.
Harris Robin Kalash:
Yeah, I didn't know about that.
Janic Duplessis:
You can add any button that you want from the menu. If there's dev actions that you need to add to your app, you can check it out. I'll try to find it.
Robin Heinze:
Genius.
Harris Robin Kalash:
Yeah, that's cool. I didn't know about that. I think what's cool is that whenever I see Janic work, he's always running his own build of React Native. That's a really cool way to discover these things. I actually started doing that after a while, except running it on the Android version was really hard, but yeah.
Janic Duplessis:
Yeah. I'm using my own fork of React Native in my apps because I usually have changes that I want to make and I don't want to wait until my PR is merged. And also, I have a couple of changes, actually that I won't upstream because they're not good enough to be up streamed. I use a different image implementation that I made quickly, that's not really upstreamable because Facebook won't change the image implementation anyway, on iOS.
Janic Duplessis:
I'm also building from source on Android, I think that makes it a lot easier to work with Android when you build from source because you can actually change the code. Like I said before, what's annoying about if you're using just React Native normally on Android, is that it uses pre-compiled jars, which is great for build speed. I'm always amazed about how fast it builds on Android when I use another app, because it uses the pre-compile React Native, but if you're building from source, it builds really slow, but you can make changes in the actual React Native core code.
Robin Heinze:
Oh, interesting.
Jamon Holmgren:
Yeah, that's cool.
Janic Duplessis:
Anyways, if you're using a fork, you kind of have to build from source anyway.
Jamon Holmgren:
Yeah.
Janic Duplessis:
That's something I've been using.
Jamon Holmgren:
You like living on the edge a little bit. There's actually some value in that though. Exposing yourself to some pain kind of forces you to learn. Once you hit those points, you got to go in there and dig in and figure out. And, it just gives you this much better mental map of what's happening under the hood in the tools that you use. I get that, for sure.
Janic Duplessis:
Yeah, that makes sense. Actually, maybe it sounds like it's living on the edge, but React Native is insanely stable. Facebook lives on the edge of React Native so they run master all the time. The issue that I have are usually more related to the open-source version of React Native. Well, there's no open-source version, but the open-source projects are structured slightly differently and sometimes bugs will happen if you're using React Native from outside Facebook versus how they use it internally.
Jamon Holmgren:
Yeah.
Janic Duplessis:
I just want to make it clear, there's no two versions of React Native. Facebook uses exactly the same code that's on the repo, it's just they have some internal modules. I think they use a different CLI though, for building the app and everything. They don't use the community CLI. There's some differences there, but there's no Facebook version. I know some people will say the Facebook user runs a different version of React Native, but they don't. They have some experiments, I guess that aren't open source.
Jamon Holmgren:
Yeah.
Janic Duplessis:
I don't want to be coded out of context.
Robin Heinze:
I'm just curious. Are there any contribution docs or something that actually explains how to run React Native from source if people want to try that?
Janic Duplessis:
Yeah, there is. I can send you the link, I guess.
Jamon Holmgren:
Okay, we can include it in the show notes. That would be awesome.
Robin Heinze:
I think that might be an interesting thing to try.
Jamon Holmgren:
Yeah.
Robin Heinze:
Just to get a better understanding and tweak things in source code and see what changes.
Jamon Holmgren:
Yeah.
Adhithi Ravichandran:
Yeah, I've never thought about doing it that way, so that's interesting.
Jamon Holmgren:
Yeah, that's cool.
Janic Duplessis:
Yeah, the main thing is you need to have the Android NDK installed because there's a lot of C++ code that needs to be compiled.
Jamon Holmgren:
That's the Native Development Kit.
Janic Duplessis:
Yeah.
Jamon Holmgren:
NDK, yeah.
Janic Duplessis:
There's the SDK and then there's the NDK, but right now it's pretty easy to install also, because you can install it right from Android Studio now. Before you had to download it somewhere, get a link, get the right version. Now, you can just get the latest version with Android Studio and it usually works pretty well.
Jamon Holmgren:
Okay.
Janic Duplessis:
There's a guide somewhere.
Robin Heinze:
Perfect.
Jamon Holmgren:
Very cool. I think we could keep asking you questions all day, Janic, but we're kind of getting to the end of this episode. So, we might have to have you on in the future some time to do some follow-up questions. So, let's go into the next part of our podcast which is, weird bugs.
Jamon Holmgren:
This is the part of the podcast where we talk about weird bugs that we have either encountered ourselves or we have seen other people encounter on our team, and we just share them with everybody. So, who has a weird bug? Anybody have a good weird bug story here? If nobody else does, I can go.
Jamon Holmgren:
A few days ago, Kevin who works on our team posted just a little heads up in our engineering channel. This isn't really a weird bug, but it's a gotcha that you should really pay attention to if you are working in React Web or React Native. He was saying, "Yesterday and the day before, I was fighting a mysterious, undefined, prevdeps.length error. It came with a warning that suggested checking the rules of Hooks, but best I could tell, I wasn't violating any of the rules."
Jamon Holmgren:
So, it turned out that he was actually violating the rule against changing the order of Hooks because he actually had an early return before one of his Hooks. So when the early return would trigger it, it wouldn't actually have the Hook that was being defined after the early return. So, you have to make sure if you have Hooks in a component, that they all happen before any sort of conditional logic that might change the order or the number of Hooks. React is very good about picking up on this and saying, "Hey, you have a problem here." In this case, the error wasn't very helpful, but essentially, you have to make sure that if you are returning from a function, a function component or you have an if statement, or something like that, that it doesn't affect your Hooks. You always want to define all of those up front.
Harris Robin Kalash:
It has to basically render the same amount of Hooks, right?
Jamon Holmgren:
Yes. If you want to be able to do it after an early return, you have to do it in a sub component. You actually have to move that Hook into a sub component and then, it's okay. So, that's something to keep an eye out for. I know a lot people have used Hooks, probably kind of know this rule, a little bit, but just keep an eye out for it. If you have an early return, that does count as a conditional.
Harris Robin Kalash:
That makes sense.
Adhithi Ravichandran:
That's a good tip.
Janic Duplessis:
I know there's an ESLint plugin for rules of Hooks. Does it not detect early returns? I don't know if it could have been possible to spot it with a Linter.
Jamon Holmgren:
I did not ask Kevin.
Robin Heinze:
I feel like a Linter wouldn't pick up on that because-
Janic Duplessis:
Maybe it's not smart enough.
Robin Heinze:
... you'd have to run it.
Jamon Holmgren:
Yeah, it's hard to say. I do know that there is that Linter and it has caught thing in the past. I don't know if Kevin is using the Linter rule or not. That's a very good question. We'll link to that Linter rule as well in the show notes.
Janic Duplessis:
That would be great if it could detect early returns.
Jamon Holmgren:
Yeah, exactly. It's one of those gotchas that Hooks are amazing and they have this really kind of cool feeling, but you do have to know quite a few rules of thumb around them because of how they're implemented. You can't just use them sort of willy-nilly.
Janic Duplessis:
Yeah, I've had a few. The worst are the stale closure bugs where you'd have... I've had one or two of those. I try to avoid as much as possible, but sometimes... It happened a few times where I was like, "Oh, that's the bug that everyone talks about." Like the stale, you'd see an old version of your variable somewhere, somehow because you have wrong dependencies or something in the Hook. I don't remember exactly what the bug was, but it was the bug that everyone was talking about that's like, "Oh, that's one of the “gotchas” of Hook, and actually hit it a couple of times.
Jamon Holmgren:
We do still use Hooks in our apps, but there's a lot of this that we kind of avoid by using MobX-State-Tree and MobX React Lite. It gets around having to think about those rules as much. There are rules with that too, but they're probably just a little sensitive.
Harris Robin Kalash:
Yeah I think Yulian, Yulian is one of the engineers ay Infinite Red mentioned something about passing an observable array as a dependency. I think there was maybe an issue there.
Jamon Holmgren:
Yeah, totally.
Harris Robin Kalash:
You have to slice it.
Jamon Holmgren:
That's definitely a gotcha there as well. Although, that had to do with an interplay between a gotcha of MobX-State-Tree and a gotcha of React Hooks. The two interplay there. Cool. Thanks so much, Janic. It was awesome to have you on dropping some of your immense knowledge here on our podcast. It's been a lot of fun and like I said, there are many more questions that we didn't get to. I had a bunch of Robin notes that I could have asked questions on. Sorry about that, Robin.
Jamon Holmgren:
Really appreciate you coming on today and very much looking forward to having you on in the future sometime. Where can someone find you on Twitter, Janic?
Janic Duplessis:
Yeah, my pleasure. Thanks for having me on the show and of course, if you want to hear me again, I'll have some fun and come again.
Jamon Holmgren:
Yeah, awesome.
Janic Duplessis:
You can follow me on Twitter @janicduplessis, my full name, you can link it somewhere because it's not super obvious how it's spelled.
Jamon Holmgren:
It'll be in the show notes, yeah.
Janic Duplessis:
Yeah, so basically follow me on Twitter. That's pretty much where I post stuff, once in a while or check out my GitHub, I guess. You should also check out my app, Th3rdwave and maybe you can link to that too.
Jamon Holmgren:
Yeah, we will.
Janic Duplessis:
I think it's a cool... Even if you're not using or you're not into coffee, I think it's a cool example of a good React Native app that you can... I've had comments about people that were like, "Oh, I didn't know your app was made in React Native." I think it's pretty much as good as a Native app.
Harris Robin Kalash:
Yeah.
Robin Heinze:
It's always good to get examples of a really nice production of React Native apps.
Janic Duplessis:
I'm also open to also to sharing stuff. I know I've shared a lot of stuff with Harris before when he was working at Uplet, so if there's something in my app that you like, and you're like, "Oh, how do you make this?" I like sharing knowledge about what I work on.
Robin Heinze:
Very cool.
Harris Robin Kalash:
Yeah, I second that. Th3rdwave is... I think I even ran your custom React Native build once, but yeah, it's a really good example of a really smooth React Native app. I actually still, I often look at it just to inspire myself and then I bug Janic and I'm like, "How did you do that?" Even the images, if you actually try Th3rdwave on a web, Janic can explain it way better than me, but it uses Gatsby and I believe you use the same image component, right?
Janic Duplessis:
Yeah. I guess that could be a subject for another time.
Harris Robin Kalash:
For sure.
Janic Duplessis:
I've built a design system component library where I can reuse a lot of code and use the same abstractions on the web and on React Native.
Jamon Holmgren:
Perfect. Yeah, thanks again and of course people can find us online. You can find our Twitter @ReactNativeRdio. We were one letter short constrains, so we had to drop the A there. You can find Harris @brunostmann. Adhithi @AdhithiRavichandran and Robin @robin_heinze with an E on Twitter. And then you find me @jamonholmgren, just first and last name. And, hit us up if you like this episode, if you have any ideas for future episodes and if you have any questions, of course, feel free to tag us on that. Really appreciated everybody coming along. Make sure you subscribe wherever you get your podcasts and we will see you next time.
Adhithi Ravichandran:
Bye-bye.
Harris Robin Kalash:
Bye.
Robin Heinze:
Bye.
Janic Duplessis:
Bye.