React Native Radio

RNR 239 - Shrink your app with ProGuard - James Hamilton

Episode Summary

In this episode, Jamon and Mazen, joined by James Hamilton, discuss how to make your Android APK file smaller using ProGuard.

Episode Notes

In this episode, Jamon and Mazen, joined by James Hamilton, discuss how to make your Android APK file smaller using ProGuard.

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:

  1. Enabling ProGuard in React Native
  2. Gradle used to come with ProGuard enabled
  3. ProGuard vs r8
  4. ProGuard vs DexGuard
  5. ProGuard Playground

Connect With Us!

Episode Transcription

Todd Werth:

Welcome back to React Native Radio Podcast brought to you by Nerds. Well, two nerds, Jed and I. Episode 239, ProGuard with James Hamilton.

Jamon Holmgren:

I don't think people realize, Mazen but you're pretty tall, you're six foot six. And you have, I don't know what that is in non-freedom units but it's pretty tall. What is it in centimeters?

Mazen Chami:

I think it's like two meters plus, something like that?

Jamon Holmgren:

A little over two meters? Okay.

Mazen Chami:

Yeah.

Jamon Holmgren:

I was expecting a little more precision than that, but let's go with that.

Mazen Chami:

Sorry, it's been a while.

Jamon Holmgren:

You're very American now after your time abroad. But also you have a very wide wing span; you're like six foot eight from the very end of your hand if you spread your arms very wide. And you're also built like an athlete because you are an athlete so I do mention all the time you were a pro soccer player, but I realized that you've never really told any soccer stories. Someday we're going to have to go through all of your semipro and pro days, but give me a fun memory of when you used to play.

Mazen Chami:

Yeah. So, the one memory we were chatting about this and it reminded me of it was of towards the end of my high school career, I knew where I was going for college and come into the US. And I want to say it was like a playoff game, it was either quarter finals or semi-finals to end off our season in high school. We had a pretty good team, we would play some semi-pro teams in Nigeria. So, I grew up in a town called Jos, so not as big, but our town professional team which I played for a little bit growing up, they were the champions of the Premier League. So, pretty good scene in Nigeria, in our town. So, I want to say it was tied and I made a save and I looked up field and I saw one of our really fast strikers and I punted the ball as hard as I could. And it bounced over the center-back's head and he ran onto it and scored.

Jamon Holmgren:

Oh wow.

Mazen Chami:

And I know we won because we ended up getting to the finals and losing two-one in the finals.

Jamon Holmgren:

Oh wow.

Mazen Chami:

So yeah, it was a great memory getting us there with that assist and as goalkeepers, you tend to just make the saves and you don't really score, well, rarely if that score a goal.

Jamon Holmgren:

Yeah, totally. So, I don't know, people may know this but I'm also a goalie at a very much lower level. I play ice hockey, I play at our local rec league and I play goalie. And I have gotten assists, what's funny is I've never noticed it. I've just looked at the box score at the end of the season and be like, "Oh, I have three assists, how did I get assists?" But often it's like someone shoots, it ricochets off, my team takes it. And in hockey they are very liberal about the assists that they'll give. So, you can have one person take the puck, go quite a ways up and then give it to someone else and they take a little bit and then they shoot and they score and the previous two players will get an assist. So, I like that because as a goalie it's very difficult but that's pretty fun. And it's always fun ... as a goalie you're so focused on defense that it's fun to be involved with the offensive side of things.

Mazen Chami:

Yeah, absolutely.

Jamon Holmgren:

Well, we've probably alienated half of our audience by talking about sports but we will move on. I'm Jamon Holmgren, your host and friendly CTO of Infinite Red. I'm located in the Pacific Northwest of the USA, I live in just north of Portland, Oregon with my wife and four kids on three acres. I'm joined today by my intelligent co-host, Mazen. I'm using intelligent this time to offset the jock stereotype I think that we just came up with here.

Mazen Chami:

We need to find a good balance, right?

Jamon Holmgren:

Right. You do, yeah. Mazen lives in Durham, North Carolina with his wife and newborn boy. He is a former soccer player, as I said, and coach and is a senior React Native engineer also here at Infinite Red. Today we have a guest too and I'm excited about this. I'm always excited about our guests, but this one's kind of fun. It's a little different actually this time, not a React Native developer, it's James Hamilton. James is a software engineer working on ProGuard and DexGuard which we're going to be talking about at Guardsquare, which is located in Leuven, Belgium. Is that where you're actually located James?

James Hamilton:

Yes, it is, yeah.

Jamon Holmgren:

Okay, cool. So, you're actually in Belgium but you're originally from London if I remember correctly here?

James Hamilton:

That's correct, yes.

Jamon Holmgren:

Also, you were a senior research fellow for CERN. Do you say CERN or a C – E – R -N? I guess, I've never heard it said outside.

James Hamilton:

Yeah, you say CERN. I was there for three years, that was very interesting time and nice to live in there.

Jamon Holmgren:

Yeah, I bet.

James Hamilton:

In Switzerland with mountains.

Jamon Holmgren:

Oh yeah, totally. That sounds awesome. I could probably do a whole podcast on that. You also have a PhD from the University of London, which means I should be calling you Dr. James Hamilton. Like me, you've also been interested in computers for a very long time and it says here that your first computer was an Amstrad CPC 464, which I've never heard of before.

James Hamilton:

Yeah, this is an old computer; it's a British computer.

Jamon Holmgren:

Oh okay, that makes sense.

James Hamilton:

Probably it didn't make it to the US.

Jamon Holmgren:

Yeah. So, this was like Commodore 64 days, right?

James Hamilton:

Yeah. Yes, similar kind of computer.

Jamon Holmgren:

That makes sense. Yeah, that's pretty fun. I actually have in the corner of my office, I have the first computer that my dad ever got which was a 286. So, I actually don't know what brand offhand, I'd have to go look at it. But it went through a house fire so it no longer works but I do still have the shell of it basically.

This episode is sponsored by Infinite Red. Infinite Red is a premier React Native design and development agency located fully remote in the US and Canada. If you're looking for React Native expertise for your next React Native project, hit us up. You can learn more on our website, infinite.red/reactnative and don't forget to mention that you heard about us through the React Native Radio Podcast.

Let's get into our topic for today. and it is shrinking your app with ProGuard. So, this came onto my radar because in the React Native documentation, there's a very brief mention of a tool called ProGuard and I mean very brief, it's just like a paragraph in a link. And it talks about how you can enable ProGuard to reduce the size of your Android APK file, which is of course the file that you would ship to an actual Android phone. It is optional. It can, now they say slightly reduce the size of the APK, I feel like you're going to probably take exception to that characterization but we'll get into that. It does this by stripping parts of the React Native Java bytecode and its dependencies that your app is not using.

So, it does static analysis of your bytecode, not just like your Java code but the actual bytecode that is generated and can strip stuff out. They do put in a little warning, make sure to thoroughly test it because are there's sometimes some configuration that needs to happen. You don't want to break things obviously. And enabling seems at least from the documentation to be as simple as enable ProGuard in release builds equals true in your build.gradle. So, that was how it came on our radar and I was like, "What is this? What is this ProGuard? Where did it come from? Who built it? What's going on here?

James Hamilton:

Yeah. So, it's a brief explanation but I guess in the documentation there, quite accurate in what they say. So, it's a tool that can reduce the size of your APK. I would say yeah, the word slightly there is maybe it depends. So, obviously it depends on your app, maybe how many, for example, third party libraries you use or maybe how much of your codes in your app is Java codes and how much is JavaScript? So, that's one thing about ProGuard is it's shrinking the Java bytecode.

Jamon Holmgren:

Java bytecode part of it. Yep.

James Hamilton:

It's not touching the JavaScript part but then it depends then how much code you have, not just of your own Java bytecode but also these third party libraries, which maybe you use, for example, one function. So, you pull in a library to use one function and then suddenly your APK expands by megabytes because you just needed this one function and ProGuard can strip out all of the other unused code for you.

Jamon Holmgren:

That's really cool. So, in the JavaScript world we would probably refer to something like that as tree shaking, is that a similar concept?

James Hamilton:

Yeah, so that's one feature that ProGuard does, basic tree shaking, yeah.

Jamon Holmgren:

Oh, okay. Tree shaking for your bytecode. Yeah.

James Hamilton:

Exactly, yeah. So, it does tree shaking which is then using the based on ... for example, unused classes methods and then it also does bytecode optimization as well. So, it will actually look at the actual instructions, analyze them, statically analyze them and then determine which bits it can remove because they're not used.

Mazen Chami:

That's pretty cool. So, if I built a pure Android app or even just a module, third party module that went through Java, let's say I had a function that I completely forgot about, maybe the function was, I don't know, checking something. Let's say that wasn't used, eventually I was like, "You know what? We're not going to use this function anymore." I moved away, used a different function. Will it go as deep as finding that function knowing that it's not being used and dropping it?

James Hamilton:

So basically, what ProGuard does is it looks at the code, it starts at certain points in the code and then tries to follow all of the link. So, if you call a method it will follow that method, call to the method and then it will look at the code in that method and it will try to then collect all of the codes which is used and then it does this tree shaking thing where then the other code basically falls away because that's not used.

Mazen Chami:

That's awesome. Okay, that's a great feature and I can see how that can strip because a lot of the third party libraries I know that we use in JavaScript specifically, one that comes to mind is Lodash. You might import Lodash for one or two functions.

Jamon Holmgren:

Famously.

Mazen Chami:

Yeah, exactly. And now, you have this huge, I use "huge" in quotes, library out there that if it was trimmed down to just those two functions would be so much lighter. Yeah, that's pretty cool.

James Hamilton:

Tried yesterday just creating a React Native sample, I guess just following the documentation in the most basic sample and running ProGuard on that saves one megabyte.

Jamon Holmgren:

A megabyte just right out of the box, just turning it on.

James Hamilton:

Yeah, it's just whatever the sample does that when you create it for the common online tool.

Jamon Holmgren:

Well, that right there sells itself. You turn it on and it works. It just reminds me: Joe Armstrong, who created Erlang, said, "You wanted a banana but what you got was a gorilla holding the banana and the entire jungle."

James Hamilton:

That's true.

Jamon Holmgren:

So, once you run ProGuard you get the banana, that's what you get.

James Hamilton:

Yeah.

Mazen Chami:

How long have you been working on ProGuard for, James?

James Hamilton:

So, I've been working on ProGuard at Guardsquare for three years now, so actually this month is my three anniversary at Guardsquare.

Jamon Holmgren:

It seems like a good fit for what you've studied. My notes have that you've studied static analysis of bytecode and have a lot of other kind experience within this realm.

James Hamilton:

Yeah. So, at university I did a PhD in static analysis. There I was looking at communities in code. So, I was looking for the connections between different parts of code and if you have dependencies between lines of code or instructions, how that looks if you think of it as a community. So, like a social network of instructions.

Jamon Holmgren:

Oh, no way. I've never thought about that before, that totally makes sense though.

James Hamilton:

Yeah. So, when I left CERN I was looking for a new job and I wanted to do something related to my PhD and this was a perfect fit.

Jamon Holmgren:

I love the React Native people community that is out there and I feel like it's awesome that you have these collisions of ideas between native developers and often web developers, JavaScript developers. One of the things that we sometimes get wrong is we think in terms of the tools we know, we think about tree shaking the JavaScript side of things. We think about all the optimizations we can do over there. We don't think about often the parts and pieces that can really or the tools I should say that can really make your native app faster because that is a whole component of this, it's marrying two different things together. But I do think it's really cool that this is Android only, I don't know if you have done any iOS stuff at this point or is it just the Android side?

James Hamilton:

So, ProGuard stuff is not Android only, it's for Java bytecode so actually, it's also for Java desktop server applications as well.

Jamon Holmgren:

I see, so it's the whole Java ecosystem.

James Hamilton:

Yeah. So, that's actually where ProGuard started.

Jamon Holmgren:

Makes sense.

James Hamilton:

But you're right, it's not for iOS.

Jamon Holmgren:

Yeah, makes sense. Then Gradle, at least used to come with ProGuard enabled, I don't know. There's some mention of an R8 compiler as well, can you give us a little rundown of what's going on over there because I hadn't heard of the R8 compiler before doing some research for this episode?

James Hamilton:

Yeah. So basically, ProGuard used to be part of the Android build system since I think about 2012 or something like that, 2010, something like that. And in the last few years, Google have been building their own compiler, which includes also a tree shaking function and optimizations. So, that's what R8 is and that's the replacements in the Google build system for ProGuard but it's completely compatible, the rules.

Jamon Holmgren:

Uses the same rules, things like that.

James Hamilton:

The rules are the same, the configuration, you say enable ProGuard still.

Jamon Holmgren:

Funny.

James Hamilton:

But ProGuard is still there so you can enable ProGuard in your app as well. You can choose between R8, ProGuard, they both have the same similar tree shaking features.

Jamon Holmgren:

And your company, the company you work for, Guardsquare did a comparison of it on the website, which was interesting. It doesn't seem like there's a huge difference between the two, probably Google just wanted more control and that's why they're pushing their thing. But ProGuard's totally still a viable thing. Is ProGuard an open source project?

James Hamilton:

Yeah. So, ProGuard is open source and it's been that way from the start.

Jamon Holmgren:

Nice.

James Hamilton:

And I think one of the nice things about Guardsquare is that it was built on top of ProGuard basically.

Jamon Holmgren:

Okay. Yeah.

James Hamilton:

So, ProGuard started as a hobby project 20 years ago. And then, out of that became DexGuard, which is the Android security tool. Out of that came Guardsquare. So now, we're protecting with DexGuard's billions of apps around the world.

Jamon Holmgren:

So, that's a whole another thing and we do want to get into DEX Card because ... DexGuard, let me pronounce it correctly here. I said DEX Card Rolodex, I don't know. But it's a security thing. And again, another thing that we need to make sure that we are doing as React Native developers, that we're properly securing our apps. I have one more question and I realize I'm hogging the questions here, Mazen but ...

Mazen Chami:

No.

Jamon Holmgren:

It just occurred to me. Are you looking at AI analysis of this stuff using actual AI tools like TensorFlow or anything like that for these types of things?

James Hamilton:

So far no, we haven't been going in that direction. Yeah. I mean, the core static analysis code index are there, it's just looking at the code and then figuring out how it works statically.

Jamon Holmgren:

So, it may not be a great application for AI in this case because it's probably a little bit more of a, I don't know how to describe it, but it's a little more of a pure function there where you can just look and see, "Is this used or not and then if not remove it."

James Hamilton:

Yeah.

Mazen Chami:

Cool. So, I know you mentioned DexGuard does the security aspect, does ProGuard have any security features within it or just what we're calling tree shaking here?

James Hamilton:

Yeah. So, ProGuard is all about shrinking optimization so there's tree shaking and then there's also the bytecode optimization as well so the actual code optimization. And then, there is the name obfuscation. It's more of a security by accident feature.

Mazen Chami:

Cool. Okay.

Jamon Holmgren:

It reminds me of Uglify for JavaScript, which basically would take pretty JavaScript code and then compress it down and change all the names into something ugly. Yeah.

James Hamilton:

Yeah, so that's basically what it does and it's one layer but it's not going to stop a reverse engineer from understanding your app.

Jamon Holmgren:

Yeah, that makes sense. So, that brings in DexGuard.

Mazen Chami:

Yeah. Can you go into DexGuard? What is DexGuard? How does it compare to ProGuard? Do they work together?

James Hamilton:

Yeah. So, DexGuard is a security tool so that's the products that Guardsquare sells for protecting Android applications. It's compatible with ProGuard so same ProGuard rules can be used. But there are a lot of other features on that and there's a lot of layers of security that are applied on top of the basic name obfuscation that ProGuard can do. And DexGuard also does dynamic protection as well. So, at one time it can tell if someone is for example, trying to hook your app, hook a function to try and understand dynamic attack on the apps.

Jamon Holmgren:

That's very interesting, so it will go through everything in your entire app control flow as well, a bunch of different things. Code virtualization, what's code virtualization? I'm looking at the list here.

James Hamilton:

This is a very nice feature of DexGuard. So basically, when you apply DexGuard it will generate a virtual machine with its own instruction set and it will convert some of the codes that's originally in your app into this new instruction set and execute on this virtual machine in your app. And then, we'll do that for lots of different bits of code in your app so that all over your app, there are different virtual machines, virtualized instructions sets.

Jamon Holmgren:

Interesting. So, it's like many virtual machines running within the JBM really, right or whatever is the-

James Hamilton:

Yeah, so then for a reverse engineer then they have to understand these generated instruction sets and it's random as well so these are generated randomly as well.

Jamon Holmgren:

That's pretty amazing. So, what types of apps might benefit from, obviously ProGuard everybody can benefit from, just turn it on, go do that. But DexGuard obviously that's more of a paid product and more of a thing that, who should be like, "Yo CTO, we need to get DexGuard on our app."

James Hamilton:

I think many, many apps can benefit from DexGuard and it's probably something that some people don't even think about. Developing this nice app and it's all working nicely and then suddenly someone has cloned the app or hacked the app.

Jamon Holmgren:

Right. Well, someone like Coinbase, I know they're on React Native, they should be using DexGuard. Obviously, you can't say if they're a client or not but that's totally an example. Anybody who's doing anything financial or medical.

Mazen Chami:

Health.

Jamon Holmgren:

Yeah. Health.

James Hamilton:

Definitely, yeah.

Jamon Holmgren:

That makes sense to me. So yeah, totally.

Mazen Chami:

Anyone who just wants to secure their user's information, especially if you're collecting that user's information, that could be very helpful.

James Hamilton:

Another use case is also gaming as well to prevent against cheaters, for example.

Jamon Holmgren:

Cheaters, yeah.

Mazen Chami:

But good to know. This is the Robin favorite question here usually on the show, but what sucks about ProGuard? What's something that ProGuard needs to work on?

James Hamilton:

I think one of the frustrating things that a lot of people find is the configuration. The configuration and the warnings that are produced by ProGuard. So, ProGuard tries to be helpful, it tells you that there are some missing classes that it doesn't know about. Because to do the analysis, it needs to know about the whole application, all of the classes that are used, all of the libraries. And it will tell you that it's missing this class, this class, this class. Sometimes there can be thousands of these warnings and this is something I think that can be improved. And then, the configuration itself. So basically, you need to configure ProGuard because for example, if you're using Reflection then ProGuard when it does its analysis, it cannot follow these trails of method calls for example, because the name of the method maybe generated at one time dynamically.

Mazen Chami:

Yeah, if you're using Reflection.

James Hamilton:

Yeah exactly, so then ProGuard cannot know if something is used or not. So, even if a method is used if you're calling it by Reflection.

Jamon Holmgren:

Explain to people who don't know what calling by Reflection means, explain like I'm five here.

James Hamilton:

So basically, you can call a method, Java or in Kotlin, in JavaScript I'm not sure. By giving it a name as a string, for example. So, you can build this string at runtime and you can tell Java to invoke this method. You give it the string, which is the name of the method and you tell Java, "Invoke this method," and the method is invoked as usual.

Jamon Holmgren:

But it's not like, you can't just look and find that symbol in the code. In fact, so just to put it in terms for the JavaScript developers. Let's say that you wanted to be able to call on a string toUppercase or toLowercase. But what you did was you had a string that was to variable case and then the variable could switch between lower or upper, depending on some if- statement somewhere. So, you would have then it's building in maybe string interpolation, it would create toUppercase or toLowercase and then you put it in brackets.

You would take your string, after your variable you'd put a square bracket and put the variable in with the name and then just invoke it with parenthesis at the end. And that would then do what it's supposed to, it's going to call toLowercase(), toUppercase(), but you can't just search your codebase for toLowercase to find all of the toUppercase. Because this is going to call toUppercase(), but you're not going to find that string, toUppercase in your codebase. So, that's I think essentially what you're talking about, James, right?

James Hamilton:

Yeah, exactly. And so, then the same is true in Java and then ProGuard cannot then determine that to uppercase or to lowercase need to be kept because it cannot see that, so then those methods may be removed.

Jamon Holmgren:

Makes sense and then your app would crash.

James Hamilton:

Yeah. So, that's where the configuration comes in. So, you need to tell ProGuard with keep rules to keep something. So, you basically say, "Keep class," whatever the method, the name of the class and then the method, the descriptor of the method, the name of the method. It looks similar to Java codes, the configuration for methods, it's a bit different because you can put wild cards in.

Jamon Holmgren:

One thing that I think is cool in the rules is why are you keeping option? It's like why is this still there? Do I have something somewhere that's calling this? I think that's pretty cool.

James Hamilton:

This can actually be quite useful because you can end up with a lot of rules like hundreds or more rules. And not only your own rules because libraries can ship ProGuard rules inside the Android AR phone.

Mazen Chami:

Yeah and this to me sounds like make sure you test your app in production too before you ship it out because very easily something that's working in development, again, ProGuard usually would probably enable it only in production once the bundle, we're talking about shrinking the APK so at that point in time, so if you're running it in React Native, you might not run into any issues because ProGuard hasn't run its features yet. So, test in production before you ship it and you never know. That's actually pretty cool. And I like the different rules that you have, it makes it very simple to use but also gives you a lot of control over how it functions, still giving you all its features at once so I think that's pretty cool.

James Hamilton:

And so I said yeah, this is one of the things that's maybe a bit frustrating about ProGuard and so we have some tools to help with this. So, last year we released the ProGuard Playground for example. So, this is our online tool where you can upload an APK or a JAR file and you can type in some rules and it will show you what entities, classes, methods, fields in your app match those rules.

Jamon Holmgren:

Oh, that's fantastic, that is really cool. You just drop in your .APK or .JAR file and then it will just scan it and then you can play with your rules to see what happens.

James Hamilton:

Yeah, exactly. Yeah.

Jamon Holmgren:

That's really cool. Yeah, for sure.

Mazen Chami:

I'm adding that to the show notes. Yeah, I'm adding that to the show notes, that's something I think every developer should play with once they have their APK.

James Hamilton:

And that can save also a lot of time instead of, like if you're trying to figure out which rules you need, you can do it interactively with the ProGuard Playground instead of typing a rule, building your app, putting it on the device, running it to see if it works or to see if the rules match.

Jamon Holmgren:

Yeah and that shortens the feedback cycle, feedback loop tremendously. So, ProGuard is on GitHub and I believe, is it written in Java? Actually ...

James Hamilton:

It is, yes. Yeah.

Jamon Holmgren:

So, do you run ProGuard on ProGuard's binary? That's what I want to know.

James Hamilton:

It's possible. I think there is even a configuration for doing that in the ProGuard repository.

Jamon Holmgren:

That's great. I love those types of self-referential or dogfooding or whatever.

James Hamilton:

Yeah.

Jamon Holmgren:

That's pretty fun. Well, I think this is ... I feel like, I don't know, with this podcast I want to expose React Native developers to tools that are maybe not within their normal wheelhouse that maybe are in a little bit considered the dusty corners of React Native like, "What is this thing that's lurking over here? I know it's here for a reason, but why, what is it?" So, I'm really glad you came on, James. One last question here, what does the future hold for ProGuard? What do you have in your plans?

James Hamilton:

Well, I think one of the things that we've been working on a lot in the last couple of years is ProGuard Core. So, what we did two years ago, we took the core analysis and framework that both ProGuard and DexGuard use and we split that into separate projects. And so, this way we give the tools that ProGuard and DexGuard use to other people that they can also use to build their own projects. And we've been adding a lot of new coding analysis functionality to that project recently because we also use that core library as part of our AppSweep service as well.

Jamon Holmgren:

Nice, that's awesome. Very cool. Well, I think that's all we have time for today but thanks so much, James, really awesome to have you on. I know there's a lot more that you could talk about here, but we always want to leave the listeners wanting more, right? If you'd like to nerd out more about React Native, check out my Twitch stream, I'm on rn.live. You can also find it on YouTube, YouTube.infinite.red. I'm actually streaming to Twitter live as well, I found a way to do that. That was cool. You can also join our Slack community at community.infinite.red. We have over 2000 React Native developers in there. You can check out the new Twitter community, which has been really cool. RNTwitter.infinite.red. Some people don't have access to this, including Robin for some reason so sorry about that if Twitter doesn't give you access but ...

Mazen Chami:

Didn't we figure out the solution to that?

Jamon Holmgren:

I thought so but then it didn't work, yeah.

Mazen Chami:

Okay.

Jamon Holmgren:

So whatever. Where can people find you online? James, where are you on Twitter?

James Hamilton:

So, I'm on Twitter @jag_hamilton.

Jamon Holmgren:

Perfect.

James Hamilton:

You can find me there.

Jamon Holmgren:

Awesome, so people can hit you up with further questions if they have any. Mazen, where are you at?

Mazen Chami:

@Mazenchami.

Jamon Holmgren:

And you can find me @Jamonholmgren. You can find React Native Radio @Reactnativerdio. Thanks again to James for joining us today. As always, thanks to our producer and editor, Todd Werth, our assistant editor and episode release coordinator, Jed Bartausky, our designer, Justin Huskey and our guest coordinator, Derek Greenberg. Thanks to our sponsor, Infinite Red, check us out at infinite.red/reactnative. Special thanks to all of you listening today. Make sure to subscribe on all the major podcasting platforms. I don't know why I say all the …, do they really need to subscribe on every single one of them? Probably just one, whatever your favorite one.

Mazen Chami:

Just one.

Jamon Holmgren:

Yeah, just go find React Native Radio on there and subscribe. You know what? I'm okay if you subscribe on ones you don't use either. Inflate those numbers, it makes us feel good. We'll see y'all next time.