00:00:09.440
talk about hot wire native. But first
00:00:14.639
I got to say something. We're freaking spoiled.
00:00:19.680
Rails developers are spoiled. It has never been easier to build,
00:00:27.359
iterate, and launch with Rails than it has been today.
00:00:32.480
Even complex UX interactions are possible without writing any JavaScript
00:00:37.520
thanks to Hot Wire on the web and Stimulus and Turbo.js.
00:00:43.200
Our feedback loops are lightning fast. Sometimes we can go just minutes from
00:00:50.160
deploying to seeing our code live in production, sometimes seconds.
00:00:56.399
And the best part is that we can ship whatever we want. There's no
00:01:01.760
gatekeepers. There's no permission needed. It truly is the open web.
00:01:13.439
But when it comes to mobile, specifically mobile apps,
00:01:20.400
suddenly the rules change. We have new languages to learn, new toolkits to master, app store rules to
00:01:28.720
abide by. All of a sudden, we've lost control, and
00:01:34.000
we're playing by someone else's rules, Apple or Google or both.
00:01:39.920
What felt effortless on the web suddenly feels impossible
00:01:46.320
on mobile. But we can't avoid mobile apps anymore.
00:01:58.159
Almost all time spent on mobile devices is spent in apps.
00:02:04.399
92% versus the 8% in a browser.
00:02:11.520
Mobile apps aren't a nice to have anymore. For many products and industries, it's the expectation.
00:02:20.480
Said another way, users live in apps.
00:02:27.520
And getting there, getting to try it in the first place. The app store and Google Play
00:02:40.319
on your laptop. Someone walks up and says, "Hey, how do I app?" And you roll
00:02:45.840
your eyes and say, "Oh, you visit my website. It's easy."
00:02:51.840
And they pull out their phone and they search for your app in the app store.
00:02:59.200
You just lost a moment there. When someone hears about your product, their first instinct is not to type a URL into
00:03:06.640
the browser. They go to the app store or they go go to Google Play and they search. And if you're not there, you've
00:03:14.159
just lost a potential customer. Outside of discoverability,
00:03:20.800
there are a bunch of things that mobile apps can do that web cannot.
00:03:27.760
reliable and customizable push notifications like interactive buttons
00:03:33.519
or dynamic content that updates after the push notification has been sent.
00:03:41.200
interactions with Bluetooth and NFC, pairing with a smart scale or a blood
00:03:48.080
pressure cuff, a heart rate monitor, controlling nearby IoT devices like
00:03:54.239
lights or speakers, an NFC, nearfield communication, tapping
00:04:00.959
a badge at a conference to check into a session, or scanning a product tag and
00:04:07.120
getting instant details on it on your phone, or rewards. integrations
00:04:15.519
with Google Fit and Apple. Log,
00:04:21.440
syncing steps, uh, tracking heart rate, logging calories, reading calories.
00:04:27.840
There's no API accessible for the web for these things.
00:04:33.280
And not to mention the real data on the device, the user's calendar, contacts,
00:04:39.199
and wallet. Deep integrations with these are possible with mobile apps.
00:04:45.040
Automatically blocking off time in someone's calendar when they register for a conference session. Inviting all
00:04:51.360
your friends from your address book by the ones you select and not dealing with
00:04:56.479
a clunky URL to share. or loyalty cards that show up on your
00:05:02.160
lock screen when you physically enter a store.
00:05:07.759
Mobile has a lot of advantages, mobile apps. So, let's say that at this point,
00:05:14.000
6 minutes in, I've convinced you. You're ready to build a mobile app, right?
00:05:20.880
Let's go. So, first we have to learn Swift, the programming language for iOS
00:05:26.720
apps. Then we have to learn Cotlin of course for Android. Then we want to get something on the screen. So we have to
00:05:32.639
learn Swift UI for iOS, Jetack Compose on Android. Once we have, you know,
00:05:38.479
stuff on the screen, we want to make sure we know it works with iOS 18 and one step back with iOS 17, iOS 26 in a
00:05:45.840
few days, like seven versions of Android because of the fragmentation.
00:05:51.360
And then we have it built. We're ready to go. We want to share it with our users. Well, now we need to get it into
00:05:56.960
the App Store and Google Play. And this is just the front end. We
00:06:02.960
haven't even talked about getting the data from our server into those apps.
00:06:09.600
So on the web, we know this, right? Getting data to the
00:06:15.440
device, the browser. We have a model that sends some data to the controller,
00:06:21.120
maybe through an IVAR. um sorry some data to the view through an IVAR maybe you're using ERB and that
00:06:28.880
renders out some HTML easy but for mobile apps we need this in
00:06:35.280
place and then we also need another layer we need an API specific controller
00:06:42.960
to massage our data from the model to get it to work in our JSON view
00:06:50.080
from there we send the JSON over the wire to iOS where we render out our screen in Swift UI and then to Android
00:06:57.039
where we render out our screen in Jetack Compose and I've skipped all the networking.
00:07:02.720
We've duplicated our work and we've left the Rails ecosystem. We now have twice as much if not three
00:07:10.000
times as much business logic to maintain and every new feature requires building
00:07:15.520
it for the web, iOS and Android. three times. When something breaks, three
00:07:22.080
times the debugging for small teams, let alone solo developers. This is a
00:07:28.240
maintenance nightmare. It's not possible. It makes native apps feel impossible
00:07:37.919
for Rails developers. Now, I know there's someone out there
00:07:44.639
going, "Joe Joe, Joe Joe, PWAs Of course,
00:07:51.759
sadly, PWA fall short in a number of categories. Outside of our circle and our community
00:07:58.800
and the developer community at large, very few folks actually install PWAs.
00:08:05.599
And the native features that are given to those that are blessed by Apple and Google are often hamstrung or
00:08:13.360
unreliable. push notifications that just stop working or getting signed out of your uh PWA for no apparent reason. It's
00:08:21.759
not unheard of for Apple to clear cookies under the hood and give you no warning from a PWA. Are they doing it on
00:08:27.599
purpose? Do they get 30% from the app store? Maybe.
00:08:34.719
And of course, PWA's lack app store discoverability, but also create an even bigger burden.
00:08:40.959
Not only do you have to get them to your website, you have to tell them how to hit that share button at the bottom and then scroll down to add to home screen,
00:08:48.240
which doesn't sound anything like add app. It sounds like adding shortcut.
00:08:54.160
Have you ever watched someone over their shoulder as they tried to do that? Someone who isn't techsavvy. Don't.
00:09:02.399
Okay. So, if fully native is over here and a PWA is over here, what's in the
00:09:09.519
middle? React Native.
00:09:17.200
React Native has some benefits. You get to share your native code
00:09:23.519
between iOS and Android. So no longer are you building things three times. You're building things twice. Once for
00:09:29.760
web, once for native. You still need all of the, you know, JSON endpoints and
00:09:35.279
stuff. So you're duplicating logic there. Sure. If you have ReactJS on the front end,
00:09:43.519
React Native is a good choice. Do not use Hotwire Native if you're already using React on the front end. It won't
00:09:49.040
give you much benefit. But if you're using HTML over the server and even Hotwire on the web,
00:09:56.480
React Native is just forcing you out of the Rails ecosystem and into the JavaScript one.
00:10:05.920
We want to we want to build mobile apps the same way we build Rails apps. Is
00:10:12.320
that so much to ask? We want to ship instantly. We want to
00:10:19.200
deploy the same way we deploy on the web and have our changes go live across all of our platforms and devices,
00:10:26.560
iOS, Android, and web, desktop and mobile. We want to own our release cycle and not
00:10:34.000
be delayed by the black box that is App Store review.
00:10:39.920
We want to keep building with Rails. We already found the tools we know and
00:10:46.000
love. Let's keep doing it. We don't want to become mobile experts. We don't want to hire teams of mobile experts.
00:10:55.120
We want to build mobile apps the Rails way. But how?
00:11:03.200
Anyone know? Of course. Hot wire native. Hotwire
00:11:08.320
native gives us access to all of this and makes it accessible to Rails developers for the first time.
00:11:14.320
Hotwire native changes the way we think about mobile development. It means that mobile apps are no longer offlimits to
00:11:20.959
Rails developers. It means that even solo team, solo
00:11:26.480
developers or small tiny teams can achieve feature par across web, iOS and
00:11:32.160
Android. So what is it?
00:11:37.519
It's just a web view. That's it. It's just a web view. It's a
00:11:42.800
web view with a little bit of magic around the edges, but that magic does a lot.
00:11:52.800
Because it's rendering the same HTML and CSS that we're using on our server, we don't have to duplicate business logic.
00:12:00.320
We can just send that same stuff over the wire to our web apps that we're sending to the mobile apps
00:12:07.440
since we're deploying from our server. We get the same stack that we're used to and the same infrastructure where we
00:12:14.000
push our code and it goes live in production. Now, just not on the web, but also in our apps.
00:12:22.560
And if we isolate our changes to Rails, we actually can skip App Store
00:12:29.360
review. Yes, we'll have to go through an initial round to get our app in the app stores,
00:12:34.560
but after that, as long as we don't touch the native code, we can continue to ship features and bug fixes that go
00:12:40.639
live immediately without Apple or Google knowing or caring and without breaking the rules.
00:12:48.720
But most importantly, we get to keep building with Rails.
00:12:54.480
We're not splitting our attention. We're not hiring new mobile experts. And we're doing it in a way that makes
00:13:00.320
it maintainable for small teams.
00:13:05.600
So, why am I up on stage today? Some of you may know me as the hotwire
00:13:11.600
native guy. Uh, I've been building hybrid apps for 10 years now with var
00:13:18.160
many variations of hotwire native which we'll talk about in a sec. And I'm here to help you take your Rails app from the
00:13:25.040
browser to the app store. I've shipped 25 of these to production
00:13:30.560
and Google Play and and uh the app store. I'm also one of the maintainers of the Hotwire native library.
00:13:37.760
This QR code is uh to my blog where I talk about Hotwire Native Weekly and have a weekly
00:13:43.360
newsletter to level up your skills with free tutorials and such. Also, I wrote the book which we'll talk
00:13:49.600
about later. So, let's see how Hotwire Native actually works. We know it's a web view.
00:13:55.600
We know we have magic, but let's pull back the covers and see what's going on under the hood.
00:14:04.240
We have our Rails server and we have our browser, our mobile web browser. Think
00:14:09.920
of this as Safari or Chrome on your iOS or Android device. And we send well HTML
00:14:15.600
over the wire hotwire. On a mobile app,
00:14:21.279
we have our navigation bar up at the top and we have our tab bar down at the bottom.
00:14:27.440
But what's in the middle? Well, it's the same thing. Instead of a
00:14:33.279
full web browser, it's an embedded web view, which means that we can send the exact same HTML that we're sending to
00:14:39.519
the server, sorry, to the uh mobile web clients to our apps.
00:14:46.480
If you take away one thing from this talk, it's this. That HTML doesn't need
00:14:51.760
to change. This is the magic of Hotwire Native. So, let's take a look at a real world
00:14:57.600
example here. I have a screenshot of the Ruby
00:15:02.639
Friends app, an app that I built to help keep track of who I meet at conferences.
00:15:07.760
And this is looking at it on uh Safari in responsive mode. So this is what the mobile web version would look like.
00:15:14.639
Here's what that app looks like on iOS with Hotwire Native and Android.
00:15:20.639
You can check this out at rubyfriend.app or download them from Google Play or the App Store.
00:15:27.040
For the native apps, we have what we talked about earlier, the native navigation bar. Up at the top, we have
00:15:34.079
the native title, the native uh three dots overflow button to show a drop-
00:15:39.199
down menu. And down at the bottom, we have a native tab bar to switch between tabs and context. Uh for those in the
00:15:45.680
back, we have my profile, my friends, and notifications down at the bottom.
00:15:50.959
But that's the Chrome. That's lower case. That's the Chrome around it. The
00:15:56.480
magic is inside the web content.
00:16:01.519
Oh, it's the same web content that we're rendering on the web.
00:16:08.720
We have some differences here where we're hiding that uh navigation bar on the web because we already have a native one.
00:16:15.199
But for the most part, we're sending the same exact we can do that with CSS. So, same HTML.
00:16:22.000
This tiny native shell offers a lot of magic for both the developer experience and the end user.
00:16:29.680
Here we have the second tab of the Ruby friends app, a listing of friends.
00:16:35.360
When I tap on a link, we get native transitions built by the
00:16:42.399
OS. Under the hood, Hotwire Native is creating a Turbo.js JS custom adapter
00:16:48.480
that hijacks that link click. And instead of doing an XHR like we do on the web, we're actually pushing a brand
00:16:54.320
new view controller onto the stack using a native navigation controller, which means we get these beautiful animations
00:16:59.920
on iOS. And not to mention these same animations, but platform specific on
00:17:06.240
Android. Your users have come to expect these transitions and they'll feel right at
00:17:12.000
home on hotwire native apps because it's the same thing. The only difference is
00:17:17.199
that the content is fully web. Along the bottom, we have our native tab
00:17:24.079
bar. iOS and Android. These are first party uh controls.
00:17:31.360
They're not it's not some JavaScript Frankenstein of
00:17:36.400
uh React and rendering out custom things. No, these are UI tab bar controller on iOS and bottom navigation
00:17:42.720
bar on Android, which means that they come with all the same features that your users are
00:17:48.799
accustomed to, like having separate stacks for each one. And when you tap on one, it pushes the whole stack back to
00:17:54.400
the beginning. They look like native controls and they feel like native controls because they are.
00:18:01.039
And it also means a hidden benefit is that when a new version of iOS comes out
00:18:06.080
or Android and the design paradigm changes, when would that ever happen? When would we get things like liquid
00:18:11.440
glass? In like a couple days. So this is iOS
00:18:16.559
26. This is what it looked like on iOS 18. The code changes to make it look like
00:18:22.320
this, nothing. All we had to do was rebuild an Xcode 26
00:18:27.679
to get the new features. Because we're just using first party from Apple or Google controls
00:18:35.039
with React Native or other hybrid frameworks where they're building their own controls, it might take weeks or
00:18:40.080
months or years to actually get something that looks and feels like like liquid glass.
00:18:48.240
Okay, so we've built our MVP. have our essentially like the the smallest amount
00:18:53.679
of work we could do to get something that looks good and submitted to the app stores. But you want to go deeper. You
00:18:59.360
want to actually use the native integrations that are built in or that you want to build yourself. You want to level up with custom native
00:19:06.400
integrations. Let's talk about two ways where we can integrate native code into our app. The
00:19:12.000
first is with fully native screens. Here we have two screens. one from the
00:19:18.160
Hey email app and one from that shows a full screen map from the app that you'll
00:19:23.200
actually build in my uh in my book. These offer two different benefits. This
00:19:28.559
home screen on the left means because it's native, the next time the app is
00:19:33.840
launched, it will load instantly. No network request required because we can cache that data. We also get native look
00:19:39.440
and feel and controls platform specific. If you slide across one of these, it'll slide off and go to the left just like
00:19:45.760
you would with any old iOS app. With the map, you avoid that awkward
00:19:52.480
two-finger scroll thing that you get on mobile web, and you get access to a more robust APIs like turnbyturn directions
00:20:00.000
or 3D rendering. Only possible on mobile apps.
00:20:08.240
Native screens are perfect for your highest fidelity and performance screens. If you're building a game that
00:20:14.559
you want to actually render out something complex, use this. You're dealing with maps or native SDKs or your
00:20:20.559
showstopper features, reserve these for the one or two things that set your app apart from the rest of the stuff in the
00:20:27.280
app store or on the web because they incur the highest maintenance cost.
00:20:33.679
Remember, you're stepping out of Hotwire native world now. You're no longer rendering a web view. You're rendering native content and with that comes all
00:20:40.960
of the rigomearroll of JSON endpoints and duplicated business logic and networking and all
00:20:46.559
that's required. But let's see how it works. Let's take a
00:20:52.000
high level approach at building that map but for Android. So we'll start off with a Google map.
00:20:58.480
This is cotlin. This is a jetack compose composable function that renders out well a Google map.
00:21:05.280
We'll wrap that in a map fragment that extends or inherits from hotwire fragment. And it gets our Chrome. We now
00:21:12.960
have our native back button, our native title up at the top, and our status bar at the time and uh networking stuff.
00:21:22.159
From there, we'll decorate this with a hotwire destination deep link URI. A mouthful
00:21:29.360
for saying an ID for this thing. This ID
00:21:36.000
we'll use on the server. So this is a JSON response on our server
00:21:42.960
that sets the path configuration for our apps. It's a way to remotely configure the behavior of our iOS and Android apps
00:21:50.000
dynamically. So it takes an array of rules where we match URIs or sorry URL patterns, URL
00:21:57.360
paths to properties. What this says is anytime a user clicks
00:22:03.360
a link that ends in /map dollar sign for the reg x apply the hotwire fragment map
00:22:09.120
URI. Hotwire native under the hood automatically sees that and doesn't display your web content. It displays
00:22:15.760
this fragment for you and now you're in native world to do whatever you want.
00:22:21.600
The benefit of keeping this on your server is that one day you decide that you don't like the slashmap and you want
00:22:27.280
to call it slashdirection. Well, you just change it on your server and the next time the user launches the app, they'll get that change and then they'll
00:22:33.760
route anything ending in directions to the map fragment. Hotwire Native is responsible for
00:22:39.360
downloading, caching, and applying all of these changes out of the box with one line of configuration.
00:22:47.600
But like I said before, native screens offer the highest
00:22:53.200
fidelity at the highest trade-off. So when the burden of a native screen is too much and you still want to take
00:22:59.039
advantage of your web content but add some native stuff around it, we can
00:23:04.480
reach for bridge components. We can add buttons to the top right of the screen in text or images or maybe a
00:23:11.840
full drop down on Android.
00:23:16.960
These bridge components are the or bridge components are the hotwire native feature that I am most excited about.
00:23:23.919
They're native sprinkles for your app. I like to think about them as stimulus
00:23:30.080
but for native code. The benefit being that they have
00:23:43.600
We'll start off with a plain old stimulus controller. Nothing new here. And when that data controller appears in
00:23:49.360
the DOM, the connect method fires.
00:23:55.039
Now, bridge components offer a third piece of this puzzle, the native side.
00:24:00.400
This would be written in Swift for iOS or Cotlin for Android. When the stimulus controller wants to
00:24:06.320
communicate to the native component, we fire this send in JavaScript. Then the native component replies back with a
00:24:12.799
primitive. The stimulus controller can say, "Hey, what is the user's heart rate right
00:24:19.919
now?" And the native component can do all of the authentication and authorization and API integr uh um
00:24:27.360
fetching to send the heart rate over as a number back to stimulus where you can write it to the DOM or make a fetch
00:24:33.279
request to pop it on your server and persist it. You're back in control on the web.
00:24:40.880
Let's build one of these live. So, we're going to build this add friend button, which you can find in the Ruby
00:24:47.200
friends app, but I think it's a plus sign now. And this button is a link to a
00:24:53.279
new friend path. We'll start with the HTML. We have a add
00:25:00.799
friend button that links to the new friend path link to. We'll add a data controller to that
00:25:08.320
bridge dash dash button. The double dash is to namespace the controller under the
00:25:13.760
bridge directory. We'll generate that controller through the stimulus generator. Bin rails
00:25:19.679
generate stimulus bridge/button which pops this button controller in the bridge subdirectory. Like I mentioned
00:25:25.600
before, I like to keep all of my bridge components separate from my web components, my web controllers, because
00:25:31.679
they serve a different purpose and it's easy to see them all in one spot.
00:25:36.880
Opening up that file, we have our stimulus controller. Nothing about this is Hotwire native or bridge component
00:25:42.880
related yet. So let's replace this import statement and import it now from hotwire native
00:25:49.440
bridge and replace controller with bridge component. This extends or inherits the controller
00:25:56.320
we just had which means we have access to all the underlying APIs of stimulus uh data values classes we can use all of
00:26:03.440
that here but we also have some new stuff like identifying this as the button
00:26:08.960
component for native apps and like I said earlier we can pass
00:26:14.320
messages along to the client by calling this send here we're saying send the
00:26:19.520
connect message to the client pass along a little context. Here we're giving the
00:26:24.799
title of the link that we just uh attached to the DOM and finally a callback.
00:26:31.840
This call back is triggered when the client calls reply to this code will be executed. Which means that when we
00:26:39.120
trigger this call back, the app will just click the add friend link and visit the new friend path. This could be a
00:26:46.240
link, it could be a button, it could execute JavaScript. It's just clicking it under the hood.
00:26:51.760
That wraps up part two. Let's move on to the JavaScript or the um stimulus, excuse me. Let's move on to the iOS side
00:26:57.919
in Swift where we create a button component that inherits from bridge component. We'll identify that with the same button
00:27:05.279
string that we had on stimulus and we'll override the only function required on receive.
00:27:12.559
This gets called every time this send is fired from the web.
00:27:17.600
Here we'll extract that title that we passed along as context as a string and
00:27:22.960
throw it into a UI bar button item. It's a button, but Apple doesn't call it
00:27:28.960
a button. It calls it an item. Doesn't inherit from button. It's a whole another thing. But these things we can put on the top of the screen.
00:27:36.159
We'll attach a call back to that just like we did on the web. This gets fired when the user taps on the button to
00:27:42.720
reply to the connect function to the connect message which will in turn click the HTML link. Finally, we'll throw it
00:27:50.480
on the screen in the upper right via the navigation item.
00:27:57.200
Once we build that, we get our add friend button up in the upright of the
00:28:02.559
my friends screen or page. But the magic of Bridge components isn't actually
00:28:08.320
building them. It's their reusability. Once we have those three pieces built,
00:28:14.480
we can start adding HTML markup to other screens like this
00:28:21.520
and have that button appear on new screens. No native code changes would be required
00:28:28.159
to add this button to a new screen. It means if you deploy your app with the native code included, you could add this
00:28:34.880
button a week, two weeks, month later, and it will just start to appear for all of your users the next time they hit this page. No app store view required
00:28:41.279
either. Bridge components are truly build once,
00:28:47.200
use everywhere over and over again.
00:28:56.000
But they aren't limited to little buttons in the upper right. No, not at all. Bridge components have immense
00:29:02.159
power. We can create native dropdowns powered by UI menu on iOS.
00:29:10.480
We can scan physical paper and use ondevice ML to turn that into a PDF
00:29:17.279
powered by Android and then get that PDF in one file in our stimulus controller
00:29:22.480
to then write to the DOM or send to the server.
00:29:28.240
We can grab the user's notification token on iOS, write that to the server, and then start
00:29:34.320
sending them push notifications through APNS.
00:29:40.080
We can use the builtin barcode scanner on Android that automatically zooms in
00:29:45.600
and adds that colored box around QR codes to show the user what they're scanning and then dismisses and sends
00:29:50.880
the URL to stimulus. or on iOS, we can create a secure app
00:29:57.279
that when you put it in the background, it locks it and puts a white screen over
00:30:02.720
all of the content until you unlock it with biometrics like Face ID or Touch ID.
00:30:11.120
You could also get to the user's location, even in the background with only one prompt instead of three.
00:30:17.679
All of these things are not only only possible with mobile apps and not on the web, but they're abstracted with bridge
00:30:24.240
components that you build it once at the bare minimum level at the most abstracted level and then you can use it
00:30:30.640
anywhere else in your on your app. To get a better feel and to see what
00:30:36.799
these really look like under the hood, you can check out bridgecomponents.dev.
00:30:42.559
I've put together 16 of these that you can copy and paste into your iOS and Android apps along with the stimulus
00:30:48.720
controllers to go with it along with full documentation for HTML about how to
00:30:54.559
add those to your app. This is pretty powerful for Hotwire
00:31:01.520
Native because this is the first time a component-like library has been released and I'm really excited to see this move
00:31:07.600
forward. So, uh, if you have any suggestions or want to submit a pull request, that's all welcome.
00:31:19.120
Okay, we've seen how it works. We've seen what it's good for. We even seen kind of the underlying pinnings of it.
00:31:24.320
Let's take a step back in time and talk about how we got here today. How did we
00:31:30.240
end up at Hotwire Native?
00:31:36.080
Back in 2016, Turbolinks Native was officially launched
00:31:41.519
by Sam Stevenson at his rather famous I can't believe it's not native talk at
00:31:47.440
Railscom Kansas City. He showed this diagram about how complex
00:31:53.360
building for mobile uh with a JS front end with a Rails API backend could be
00:31:59.919
and then blew it all away by building probably the first ever live demo of
00:32:05.919
Turbolinks Native turning Base Camp into an iOS app live on stage.
00:32:13.600
From there we've gotten two major jumps. one in 2020 with the rebrand to Turbo
00:32:20.480
Native which was a rebrand. Uh no new
00:32:25.519
features except that TurbolinksJS became TurboJS. So Turbo Native wanted to
00:32:30.559
follow along but it was still the same clunky uh very verbose API. You had to do a lot
00:32:37.919
of boilerplate to get everything working. It scared a lot of Rails developers away and rightfully so of
00:32:43.440
doing native development. But in 2024, we saw the first release of hotwire
00:32:49.919
native. Jay from 37 signals announced this right
00:32:55.760
before Rails World last year. And this brought web develop mobile app development to Rails developer in an
00:33:02.240
accessible way for the first time ever. What took hundreds of lines of code to
00:33:07.679
build a real working iOS or Android app was now down to like 12 or 15.
00:33:14.159
As an example, here's what it used to take to build a turbo native app on iOS.
00:33:19.840
We'd have our scene delegate, assign a window, pop it out with a custom turbo navigation controller, which subclass
00:33:25.200
navigation controller. On the load, we would hit the local host, make sure to visit the visit options, have two sessions, make sure we popped it on
00:33:31.360
visit, and then make sure that we handled errors, and of course, if the session crashed, we'd reload it. Easy,
00:33:40.880
but not attainable. Also, all of this is boilerplate. If
00:33:47.200
I've built 50 hot turbo native apps, 48 of them had this exact same code in it. There's no need for everyone to be
00:33:53.200
writing this over and over again. With Hotwire Native, that turned into
00:33:58.399
this. This is now all you need to get started on iOS to build a Hotwire Native app. 11
00:34:06.080
lines of code, which you could even shorten if you put the whole private navigator thing on one line. But this
00:34:12.560
means that we can now expect Rails developers to build mobile apps because the Swift required and the Android
00:34:18.800
required, the Cotlin is is no longer overwhelming. Hell, it even looks pretty.
00:34:27.359
Hotwire Native also gave us new documentation. For the first time, Hotwire Native is a first class property
00:34:35.919
or entity in the Hotwire namespace. We now have turbo and stimulus and now native up at the top.
00:34:42.000
these documentation. This doc site is uh brand new custom branding, open source,
00:34:47.200
and also includes guides specific to iOS and Android on how to get started.
00:34:52.720
If you leave today and you're like, where do I go next? native.hotwire.dev.
00:34:58.320
Check out the how the um iOS and Android getting started guide. And that will walk you through taking your Rails
00:35:04.320
server to iOS and Android and really seeing what Hotwire Native can do in 12
00:35:09.599
lines of code.
00:35:15.040
We also introduced new demo apps more powerful than ever. These demo apps
00:35:20.880
include uh native tab bars along the bottom which are now included in hotwire
00:35:26.079
native 1.2. Uh they handle three different bridge components that you could copy paste
00:35:32.240
into your app if you wanted. They also show an example of how to go from native screens and back again to the web
00:35:37.359
context, giving you the full picture of how hotwire native works.
00:35:43.839
These hit a demo server that lives forever at a URL. So when you launch
00:35:49.599
this, you can hit that demo server and not have to worry about running anything locally. But before this demo server was written
00:35:56.320
in ExpressJS, which created some confusion. How do we redirect? Where's
00:36:01.359
the Rails code? What about authentication? Well, with our latest release, we've now
00:36:06.560
migrated that demo server to be a Rails server, which means that you can build out complex interactions like this.
00:36:20.160
And all you need on your server is redirect to. So, what's going on here is that we're
00:36:25.680
presenting a form. I'll wait till it starts. We click our link.
00:36:32.880
It presents from the bottom as a modal. We fill in our information. We have a bridge component in the upper right showing a native submit button that
00:36:39.599
dismisses the screen and pushes a new screen on the stack and we get our flash message for free.
00:36:44.720
Hotwire native is now integrated into Rails in a way where you don't need to do custom routing on the server just to
00:36:50.240
get it to work. Simple redirect twos, flash messages, modals, they all work
00:36:55.280
out of the box. and all of the different scenarios that are now handled.
00:37:06.400
All 15 and more cuz I couldn't fit one slide um are now handled out of the box
00:37:11.760
on hotwire native which means that you don't have to worry about if you're going from a modal to a non-modal to a
00:37:16.880
stack to a pop to a replace. You just pass along the context and hot native will take care of it for you.
00:37:25.920
We're also getting a bunch of new features in Hotwire Native 1.3.
00:37:31.119
Included in that are uh custom animation to fade when the screen replaces itself.
00:37:37.839
Tab bars that load lazily instead of loading them all the time, potentially reloading uh overloading your server.
00:37:43.200
Customizable error handling with native views with just one call back.
00:37:48.480
There's about six PRs open on the Hotwire native iOS repo. Take a look if you want to see more.
00:37:55.280
It might not make it to 1.3, but what I am most excited about is offline caching.
00:38:02.480
This is easily the single most biggest blocker I have when working with
00:38:07.680
clients. They are all in on hot wire native. They cannot wait to get started and then we get to like signing the
00:38:13.599
contract and they're like, "Great. How do we do offline mode?" And I have nothing. Offline caching will give us access on
00:38:20.480
Turbo.js JS where when we visit a page it will store in our cache using a PWA
00:38:26.000
like manifest and then when we visit that page it will load the cached copy if we lose internet access we'll be able
00:38:32.640
to configure it with this taking advantage of a service worker
00:38:39.520
and to get it working for hotware native apps native true
00:38:47.440
so excited here uh Rosa is working on this in PR 1427,
00:38:53.119
but more importantly, she's speaking tomorrow at her talk, bringing offline mode to hotwire with
00:38:59.359
service workers at 1:45 tomorrow. I'll definitely be there and I encourage you to as well.
00:39:10.640
So, we've seen how it works. We've seen all the fun stuff that's coming, all the
00:39:16.320
stuff that we've how we got here. But who's having success with it? Who is
00:39:21.520
taking advantage of Hotwire Native and seeing real business benefits from it?
00:39:26.960
Let's talk about a few folks in the community who are using Hotwire Native to their advantage.
00:39:40.320
optimizes CPAP therapy for folks who suffer with sleep apnea. This iOS app integrates with custom
00:39:48.240
Bluetooth medical devices and also has an integration for Apple Health and Apple Watch. He's combining all of this
00:39:56.240
data that he could only get from a native app and presenting it in beautiful charts and graphs for the
00:40:01.760
user. Charts and graphs that are rendered on the web where it's easier to render those things. Chart kick,
00:40:07.119
chart.js. JS only n only only native apps have access
00:40:13.599
to these integrations. And if he had to build the whole thing in React Native or uh fully native, he
00:40:22.400
would lose all of the feature parity of the you know hundreds of models and controllers that he would get when using
00:40:27.680
hotware native. Native sprinkles only when needed.
00:40:33.440
We have 37 signals, perhaps the poster child for Hotwire Native with their three apps, Base Camp, Hey Email, and
00:40:39.920
Hey Calendar. If you're looking for a really good example of what Hotwire Native is and can do, check out any one
00:40:45.839
of these three apps. They are pushing them to the limits with native home screens, native interactions, and
00:40:51.920
already using a bit of offline caching. But why I bring them up is that they are
00:40:56.960
using these to scale to millions of paying customers.
00:41:02.560
These are their flagship apps. They chose to use Hotwire Native again for Hey Email and Hey Calendar when it came
00:41:08.880
out. They didn't migrate off of it. It's not a pet project. Hot native is
00:41:14.640
scalable to millions of dollars of revenue and beyond.
00:41:25.520
Next up we have Nadia from the story graph. She's built a Goodreads competitor that
00:41:32.640
has access to a bunch of native features like push notifications, uh, native tab bars, and even inapp purchases, which
00:41:39.359
are notoriously the most difficult to build on iOS and Android apps. Hotwire native aside,
00:41:46.079
she's scaled to 4 million registered users. But the most exciting part is that she's
00:41:52.160
doing it as a onewoman dev team.
00:41:57.280
Hotwire Native unlocks mobile apps even for the smallest of dev teams all the
00:42:02.720
way down to a single solo developer.
00:42:10.560
I talked about Ruby a little bit earlier on the way home from Rails comp. I actually built the prototype for this uh
00:42:18.000
way to keep in touch with friends that you meet at conferences, you know, scanning badges and stuff like that. integrated a bunch of native features in
00:42:24.400
the iOS and Android apps like push notifications, QR code scanning, and NFC reading and writing. All with of course
00:42:30.720
Bridge Components all also available in that bridge components.dev library I was talking
00:42:36.400
about. But why I'm talking about this is that this went from idea to in the app store
00:42:43.920
in just two weeks.
00:42:49.760
Hotwire Native is finally bringing back the speed of web development that we're used to to mobile apps.
00:42:56.800
Apps that used to take weeks or months can now take days
00:43:02.240
if you're building with Hotwire Native and just reusing your mobile web screens and even going beyond MVPs and adding
00:43:08.960
native features with pre-made bridge components.
00:43:16.319
These are some other apps built with Hotwire Native by the community
00:43:21.440
available in either Google Play or the App Store. Some developers of these apps are in the audience. I see.
00:43:36.640
With Rails, the web was just the beginning.
00:43:43.520
With Hotwire Native, mobile apps are now
00:43:50.960
with Hotwire Native, we can launch instantly less than 20 lines of Swift or
00:43:56.720
Cotlin to take our Rails app from the browser to the app stores.
00:44:08.880
With Hot Native, we can keep writing Rails. We don't need to become expert
00:44:14.079
mobile app developers. We can ignore the minutia of iOS and Android app development and just stay in our comfort
00:44:20.319
zone. With Hot Native, we can deploy when we
00:44:26.720
want. Ship a feature and instantly see it accessible across all three platforms.
00:44:33.200
No one stopping us from iterating daily or hourly or every minute
00:44:40.400
because we can ship without gatekeepers. No waiting for review, no mystery
00:44:46.800
rejections, just our web content in mobile Chrome.
00:44:53.359
With Hotwire Native, we can finally own the full stack.
00:44:59.760
It's quick enough for a tiny team or even a solo developer to get started, but robust and extensible enough to
00:45:07.119
scale to apps with millions of dollars of revenue.
00:45:14.720
Now, get out there and ship. But before you do, if you need a little
00:45:20.079
bit of help, this is my book. And for everyone in the audience today, a tiny 50% off discount
00:45:28.640
with RailsWorld Joe. That cut off. That's supposed to
00:45:33.760
say Rails World Joe. Um, and yeah, come talk to me if you want to talk about Hotwire Native. Thank you so much. Thank
00:45:40.079
you to the Rails Foundation. It's been a pleasure.