How Discord achieves native iOS performance with React Native
blog.discordapp.com
blog.discordapp.com
However, in my experience you get diminishing returns once your app has passed v1 and your customers expect more after the new-ness wears off. You find yourself spending more and more engineering effort to make your RN app to feel as good as existing mature native-first apps, which is especially frustrating as you are solving problems that native-first app developers never had to solve in the first place. They got the solutions for free with the native sdks.
This plateau of developer effectiveness is hard to get away from as your app becomes so coupled to RN and your team invests so much into RN tooling and skills that it feels like a sunk cost.
I do not envy the Discord apps team.
For me it isn't about look and feel so much as being able to quickly manage massive amounts of data for interaction and data viz. I use the ramda lenses, sagas, redux, and selector stack to manage state of the data. It is clean and quick to develop. Everything is memoized with selectors only changing if the reference to the object it is using on the state tree changes.
Nobody can work data the way I can with React. It makes sense.
You inevitably must solve problems on Android that iOS has not yet solved (or are trivially easy to do) and vice versa, but each framework also has its own strengths that come with it.
React Native is no different in this regard. We sometimes solve different problems that occasionally area easier to do on Native (although as mentioned by this post, this is the first time in years we've actually had to sit down and focus on performance).
That being said, some of the pros are that we save a tremendous amount of time through code sharing, web team contributions, etc.
It’s trivial to interface existing native code with react-native. I’ve “merged” two existing apps written on both platforms into react-native apps by adding only a small amount of JS (and some native RN interfacing) with removing zero native code on either platform. Likewise, I’ve started with full Javascript apps and slowly replaced the JS with native code where perf mattered or RN was lacking.
You can choose how much “react-ness” is in your app quite easily, and to me this is the real game changer for react-native.
[0]https://facebook.github.io/react-native/docs/integration-wit...
I’ve always loved the framework and the beauty of being able to write native , cross-platform applications as a web developer. And what they are doing right now, trying to get rid of the js-thread bottleneck in 2020 excites me a lot!
> “While it was closed it always had a spinning animation in the background chewing away at CPU — death by the smallest cut!”
I spent an entire day doing the same! I had a spinner running in the background that ate 30% of my battery! I felt so stupid back then. :)
Thanks Discord for sharing this, the community really appreciates it!
The non-obvious one is why is the framework consuming any CPU time at all on something not visible? When an element tries to animate, and the whole element is covered/offscreen, the framework should simply pause the animation. When the element becomes visible again, it's simple to calculate how long the element has been invisible, and fast-forward the animation to that frame.
However, if using something like native navigation, where view controllers and their life cycle get handled by iOS then it will correctly end an animation.
I have no idea if this is true for Android. But I have witnessed it on iOS and fixed this issue in react-native apps on quite a few occasions.
The ultimate downside to frameworks like this is they imo, don’t know enough about the content they’re showing to make great choices for users. But at the same time, it’s not fully desirable to attempt to assume. That’s the platforms job. And that’s an obvious catch-22. How should you know this if you aren’t a native app builder?
My impression has been that to use RN well you need to mostly think like and have the knowledge of someone who is a native app developer. You just get some benefit of sharing UI across platforms, but still need to do many things you would for native.
I haven't used RN, just saw my friend who uses give a talk on it. So I certainly may not understand exactly how much native code you can or can't write while also using RN.
It greatly augments your ability to write elegant solutions to hard problems.
Some argue this is a large cost to bear - however I'd say it's one that is very reasonable to acquire. Having experience and knowledge in several languages/frameworks helps make one stronger engineer.
Started with Expo, great tool if you don't have native modules. But i have, very complex search library in Rust. After few attempts the code was converted to asmjs (RN doesn't support wasm). So i have JS-only codebase that works on Expo. Livecoding (a bit slow beause of large js codebase) and easy deploymnet on all platforms.
After release users starts complain about performance. RN fetch API doesn't support binaries, everything passed back and forth as base64. Some low-mid tier phones got stuck for few minutes. Also RN fetch doesn't support progress and a lot of other thing.
Decision was to start using native libraries. Ehh, goodby sweety Expo...
The lesson was learned, if you have complex application don't expect too much without heavy investment. JS is ugly and unpredictable as usualy. Overall experience is ok/mehh, but not great.
For the next project i give Qt5 a chance.
- Tooling for Linux was horrible, like really! - Alien mobile UI look and feel - One can not simple build mobile app for desktop. - New shiny search engine prototype doesn't fit into low tier Phone RAM. - Mono Hello world application on MIPS device with 128RAM uses about 28 mb of RAM. - 400 MHz cpu have really hard times with GC, nogo for soft-realtime. - No seamless integration with native code, using libs like ffmpeg is crazy hard. - F# was 2nd citizen in that time!
Now things changed, but still imho Linux is not a good choose for .net, also MS force you to use Azure/Cloud/etc infrastructure.
.Net doesn't force Azure/Cloud at all. I know all the MS demos tend in that direction, but you can absolutely use it all internally. I've worked on several backends targeting docker and linux with .Net core at this point. Even ARM as a target is decent. GUI, no idea on that, I tend to prefer react+redux+material-ui there.
I have intentionally kept my very specific use case mobile app very very simple and not gone deep on the magic edge cases. It is a private app and will never be on the store. It is basically used as a QR code scanner to track equipment.
I'm sure that if you have a lot deeper/larger apps you'd find more to complain about, but so far I'm happy enough with going from zero to full fledged app in about a month of work.
IMHO, the boilerplate argument with Java is kind of outdated (Project Lombok and modern java solve a lot of that).
- write once and compile all business logic (and server and fs i/o ideally) into a portable independant libray, either in something that compiles to C, or wasm (once it’s more mature)
- write native UI code using native tooling, and call the business logic library from there.
But i haven’t settled yet on the ideal PL to write that library and have it interface easily on both android and ios.
I suspect it will be the best way going forward, other approaches all have serious issues.
- It forces you to think about your business logic vs. UI structure.
- It forces you to make your core logic library easily testable.
- Since you can deploy the core logic library anywhere, you get native, no Electron bullshit, support for pretty much all major operating systems at a cost of running a CI on all compilers to catch non-portable parts of C++ quickly.
- The users get UI that's built for their platform with it's own quirks and doesn't burn 500MB of RAM.
- Using a serializable and portable message standard (we opted for Protobufs) to communicate between logic library and UI part makes deployment to server easy as well.
Biggest issues?
- Getting inflexible people to learn anything other than JavaScript.
- Getting slow compile times under control.
- Getting CI coverage to the point where it catches developers accidentally adding Apple or Linux specific headers into codebase.
- Learning to be conservative enough to not trust the platform and ship our own libraries. Even basic ones like SQLite.
We used C++14 which has a pretty good standard library and plugged the holes with some parts of Boost and then standalone libraries like SQLite, json11 and others to provide missing functionality.
I believe it was due to JNI but my memories may be wrong.
Electron is a UI 'framework', so bringing it up is kind of a non sequitur. Your approach of a C++ core is entirely compatible with an Electron UI.
I worked on a project that had its 'core logic' implemented in C++, which we then ran that on the web in Javascript, for the same portability reasons.
We actually used their own Djinni library on one of the projects and it worked well - but even at that time the Dropbox team wasn't maintaining it at all since they never used this approach beyond the short-lived Carousel app.
So apparently the experience never gets really good as opposed to native iOS development where once you get to a certain level you don't spend that much time chasing weird build errors.
There has to be a better way to write mobile applications. Is that Ionic? I don't know.
That said, who knows what the demo app was done with. If there is lagging scroll performance, it could be a lot of things.
I will say that figuring out where the performance problems are would be pretty enjoyable in IDEA. The tooling there for performance is quite good.
https://developer.apple.com/tutorials/swiftui/
There may be a chance of it being adapted for other platforms in the future.
Maybe other developers who really really love Swift and SwiftUI might port it to their preferred platforms. :)
that’s not the hard part, and apple knows it ^_^
it will be interesting when/if someone manages to get something close to apples implementation working, though my guess is they will have a “not fun” time trying to reimplement every ui interaction satisfactorily...
Flutter looks interesting, going to check it out.
In the end one wants a true "write once, run everywhere" framework, not write the business logic and then write the UI 3+ times for each target platform. Ionic delivers on this front, but am curious to see if Flutter takes it to another level (blazing fast dev/reload cycles and native performance in production sound quite appealing).
What is wrong with react natives official documentation? I think what many mess up(me included) is to look at the wrong version of the documentation. I guess that could be a little bit more obvious.
edir: oh, typescript! Yeah, that is a little bit more troublesome; https://facebook.github.io/react-native/blog/2018/05/07/usin...
If you already know React/Redux/etc, perhaps.
Else "Ready to go" after reading about several concepts, libraries, and systems and getting how the work together, what performance pitfalls they have, etc...
I guess I’m asking; what other solution have you found that solves everything for you?
It was from someone familiar with GUI programming, who wants to try doing it in React Native and suddenly finds tons of new concepts to keep in mind (compared to common native GUI frameworks/libs).
You can bring in libraries of course, but even for a large iOS project it's unlikely (and probably unnecessary) to have more than a handful.
So appending '--typescript' is troublesome and not obvious? Like this?
react-native init AppName --typescript
That's it. I never knew in 2019 that adding an extra flag would be 'troublesome'. The description of your installation of a React Native and TypeScript project in 2018+ is greatly exaggerated.
Nothing worked.
Is the flag generating a project that works without anymore tweaking/setting up? If so, my reference to the blog is not accurate.
Also, a better reply would be “oh that article is not accurate anymore, now everything is setup for you I’d you add this flag!”
For us at Standups[1] RN has been just instrumental. As a small team, we are able to ship features, just like Discord, vertically all across the stack. Having 1 language in common (TypeScript) all across your stack is the best for small teams/startups. We share most of our code.
For those of you interested in how to share code vertically using TypeScript[2], check out this guide by Bruno Lemos, super helpful, great value there for startups and especially tech solopreneurs.
[2] https://dev.to/brunolemos/tutorial-100-code-sharing-between-...
My company has both natively developed Android and iOS apps, as well as a few RN apps. I'm in the developer tooling part of the company. Indeed, the RN apps did allow those teams to add developers who had only JavaScript coding experience. But at the cost of a lot of additional complexity elsewhere in the company. Now we effectively have to support three platforms in our build system, in our SDKs, and so forth.
I'm fairly confident that it is not a net win. Those RN developers eventually have to learn the weird idiosyncrasies of both Android and iOS besides the idiosyncrasies added by RN itself. They now have bugs that show up on only one platform because RN Android is using a different JS engine than RN iOS.
There's just a gigantic amount of complexity added by RN on-top of the gigantic amount of complexity you already get with Android and iOS.
In the end, at best you end up with a mediocre cross-platform app. I think RN probably has a sweet spot: a small to medium sized app developed by a sole or maybe a few developers. Once you're big enough to have teams of developers, I think RN is a mistake.
With respect to this article, the amount of work and expertise in both RN and iOS necessary to achieve their performance goals seems pretty extreme ("mobile performance squad"). And this was all just for iOS? Did any of it translate to their RN Android app? It's a counter factual so who knows, but I suspect the performance problems wouldn't have occurred on a native app in the first place, and if they had, would've been straightforward to track down in Instruments. They claim all this work is offset by allowing their web app developers to contribute to the RN app and that it allowed them to develop the iOS app more quickly, but I have my doubts. As it is, they've got three developers dedicated to the RN iOS app alone and despite that, still ran into performance problems.
Again, maybe they have enough shared code and developers to make it worthwhile, but I dunno.
In our experience RN loses, for our kind of applications (so I am not saying this is universally true!), from all 3 the above mentioned technologies in cost, time to implement and long term stability. We have apps running for many years while requiring fixes/feature additions in the meanwhile and that cannot cost a lot as they are small features/fixes. Requiring to refactor/fix things after updates of the platform is just not an option as we do not have many resources. We had incredibly bad experiences (in backend tech, so apples/pears but still) in that regard with Ruby/Rails and js/npm; that is great for big dedicated teams with continues development. We had great experience with ASP.NET (old+new) and Django; stuff just works after years and not much knowledge is needed to go from one major version to another (it did not take us much work to go from asp.net to asp.net core for instance).
Flutter definitely seems promising ; the foreign function interfacing is much better than RN (imho) and it just seems less like a mess. We will keep evaluating Flutter while working on/with Xamarin Forms (apps), Avalonia (desktop) and Livecode (everything).
This is for LoB applications like you seem to target as well; for consumer facing apps, we write native on all platforms, but that really does not happen often in my line of work.
Ha, you know React runs in web browsers, right? React developers should already be used to having their code run in various JS engines (Gecko/Blink/Webkit and SpiderMonkey/V8/Nitro).
The amount of work was non-trivial, however, in absolute time it was a few weeks of work that we have only done once after first creating the app many years ago.
It's likely that we we would have considered doing something like this even if the app was in pure iOS - Discord has gotten much bigger.
Mobile teams I have seen for apps at scales similar to Discord tend to run anywhere from 10-20+ dedicated engineers. By comparison I believe we have been able to accomplish a lot with a much smaller team.
The lingua franca options are effectively Xamarin, Flutter, and React Native. Xamarin's dev experience sucks and Flutter's perf isn't much different from React Native with a way less mature ecosystem.
There are people who should write platform-native mobile apps. For everybody else, React Native is fine. And, for my money, TypeScript is the most right programming language easily available on major platforms today, so that makes this one pretty self-evident to me.
I'm not sure _why_ this should be theoretically bad. It depends what you're going for, of course, but cross-platform GUIs are typically... not good. There's a lot to be said for separate GUIs with shared business logic IMO.
Android is no longer a laggard with features, which was also a big goal of ours.
That said, if I were to go back and time and know how much of my own time would be spent fixing bugs in react-native just to keep the team productive, I might have made a different choice.
Edit: should also be known that I’m no longer helping with fixing RN bugs, and the team still messages me on occasion to ask questions or for help to fix things. This was absolutely not the position I was trying to leave that team in.
Edit edit: my leaving the team wasn’t within my control and management didn’t realize the level of work to Keep the Lights On nor did the particularly care.
Pssst ...
It is quite interesting to see that the re-frame and fulcro authors were on the right track adding their own 'componentNeedsUpdate' layer which is very fast and will minimize the repainting React has to do. (Mostly thanks to immutability)
Now please make discord in windows start up faster than it takes to boot windows. That's right cold starting my computer and logging in takes less time than starting discord.
1) Weird issue where it reconnects you on across multiple devices. I was on my phone... then switched to computer. Phone was no longer active. About 5 minutes after I got done with the game, Discord on my phone kicked on all by itself. Phone was sitting in another room, and all of a sudden Discord chat was being broadcast. (Luckily PTT was enabled so they didn't hear me.) It shouldn't try and reconnect... once you hang up once, it should know to hang up across all devices.
2) Can't upload profile or server images more than twice before it triggers some sort of internal cooldown on uploading images. Just puts you on the same page and pops a warning, "You have unsaved changes!" but won't let you save. Quit, wait a few minutes, try again... works. Annoying when you crop something wrong and have to re-upload. It should always let you upload, or give some message, "Still sending out your last image to all users, try again in 5 minutes."
3) Can't resize Window via window manager (Magnet on MacOS). I think this is the only program I have seen that won't let me drag and snap it. Annoying.
This is about my only gripe with Discord though, it's a great service.
This is primarily an artifact of history for us. When we first set out to build the mobile apps, React Native for Android had not yet been open sourced so we had no choice but to build it in native.
That being said, the Android side of RN has always been more nascent and so one of the benefits for us has been not having to deal with some of those early bugs/issues while building our our current iOS app.
It's since become much more mature however - so we may explore RN for Android again someday.
It's made with Qt, I believe.
I could understand if they used the same app for both platforms (like they do with their Electron app, which is also not as good as Ripcord), but since they have a native app for Android...
And only iOS + web (+ desktop?) are mentioned in the post.
I'd imagine the reason they didn't use RN on Android is because historically it's always had much worse performance on that platform. iOS RN performance is usually acceptable, but Android has always been a bit of a 2nd-class citizen.
I try to stay away from that company as much as I can, but since users unfortunately use that company.
We chose React Native because it was a pragmatic choice and the best tool for our situation. As our startup gained traction and we decided to build mobile apps - we already had a React JS app so it made sense to try out RN.
As it turns out, we were able to build an almost feature parity versions of the React app in a weekend that shared all the business logic - so it made a ton of sense for us.
React Native for Android did not exist at that time (not open sourced) and so I had to build out our initial Android app in native - I can tell you it took me a lot longer than a weekend :P
That isn't to say there are not pros and cons to each approach - but I think it's important to be always open to new tools/languages vs. going with the "devil you know" so to speak.