Add "What I'm enjoying using for mobile app development"
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful

This commit is contained in:
Kaan Barmore-Genç 2024-01-02 00:10:19 -06:00
parent 0bcc672bbe
commit be2e215b39
Signed by: kaan
GPG key ID: B2E280771CD62FCF

View file

@ -0,0 +1,208 @@
---
title: "What I'm enjoying using for mobile app development"
date: 2024-01-02T04:33:55Z
draft: false
toc: false
images:
tags:
- dev
- react
- mobile
- typescript
---
I like baking bread. Well, most of all I love bread, and baking it myself is
more convenient than tracking down a good bakery nearby. I used to bake a lot
--and as a hipster I'll tell you that it was before everyone else started doing
it at the start of the pandemic-- but I got bored of it some time ago and quit.
But I've been eating more bread recently, and decided I could bake something
better, and cheaper, than the bread from the grocery store. I'm pretty happy
with what I've been baking too, they are all turning out with a good crust and
soft insides. What's the inside of the bread called? Anyway, my bread is good
but a little bland, so I decided to also get back into sourdough bread to get
some nice flavor.
Sourdough bread is made with sourdough starter, a live culture of yeast and
bacteria that needs to be maintained with regular feedings of flour. So before
you can bake sourdough bread, you must first prepare your starter and grow it
for a few weeks to get its strength up, then keep it alive for as long as you
want to keep baking. And before you can prepare a sourdough starter, you must
first develop a mobile app.
Well, of course I could have just set up reminder in my calendar and skip having
to develop an actual app. Or just use on of the proprietary ones. But why do the
easy thing when you can waste tens --if not hundreds-- of hours developing an
app to maybe save a few seconds of your time or to make a point about using open
source software? At least it will teach me some stuff along the way.
## What tech to use?
But before I could start developing an app, I had to decide what to build it
with. My first two requirements: it had to be cross platform, and it had to be
able to show scheduled notifications on my phone. My first thought was to use a
PWA. PWAs are web apps that can be "installed" on your phone like a regular app,
but run entirely on your browser. It's sort of an Electron-lite. That would be
great since I already know a lot about web development, alas PWAs can't send
scheduled notifications while the app is in the background. That's a pretty
vital part of this project so that rules out PWAs. Also I think Safari has some
problems with PWAs? So that also would have ruled it out if I somehow worked
around the notifications.
Similar to PWAs, I could have used something like Electron to develop an app
using web technologies. In this case it would be an actual app, so I could
actually run code in the background and send scheduled notifications. Hurray!
Except that I wasn't sure I wanted to do this. My experience with apps developed
this way is terrible, and it's often very obvious someone basically put a web
browser in kiosk mode. These apps are often slow, the UIs are completely devoid
of the native look and feel, and they are often full of weird browserisms like
being able to hold and select the text on the page. Maybe this perception is a form of [Survivorship bias](https://en.wikipedia.org/wiki/Survivorship_bias#In_the_military)
and there are lots of apps I used developed with these technologies that work great,
but I decided to see what else was available first.
After more digging, I finally narrowed my choices down to 2 options: React
Native and Flutter. React Native takes the usual React, and instead of rendering the virtual DOM into a real DOM it renders it
to native UI components. React Native is something I used before, with my [Bulgur Cloud](https://bulgur-cloud.github.io) project.
While I liked React Native, I actually ended up rewriting Bulgur Cloud --in Next.js-- so I was a little hesitant.
Flutter on the other hand uses Dart, and is developed by Google with a focus on cross platform apps.
Flutter works by rendering everything to a canvas... which sort of surprised me.
My biggest concern with this was performance and accessibility, using native components
gives you both out of the box. If you render everything yourself, then it's
your responsibility to handle accessibility and optimize rendering.
Articles and discussions I came across online reassure me that Flutter is "doing better" at all of these,
so hopefully it's not a problem.
At this point I was still paralyzed by this decision, so I ended up creating a project with both to check out
the out of the box example. Which really did not help, they both work and look fine.
The code is pretty easy to understand with both. I have concerns with the future of both projects,
because React Native feels too under control of Expo and Flutter feels too under control of Google.
I finally broke my paralysis by just going with React Native. I already know
React, I know a ton of stuff about the React ecosystem, I've already used React
Native before. Flutter and the Dart language look fine and I'm sure I could
learn them, but it's just easier for me to get this project off the ground if I
use what I already know. I also remembered that the reason why I rewrote Bulgur
Cloud was because React Native for Web is a bad experience, but in this case I
don't care about the web so it's not a problem.
## To Expo, or not to Expo?
When you start using React Native, the first thing you see is Expo. Look at the
official getting started guide for React Native and it immediately recommends
you use Expo. Expo then keeps coming up again and again in the docs. It's easy
to see why:
- Expo solves a major getting started hurdle: installing Android Studio or XCode
and connecting it to your phone so you can run it. Instead, you install the
Expo Go app from your phones app store and scan a QR code to get started
immediately.
- Expo comes with a lot of built-in packages. If you go with Expo, you can use
all these packages *and* you can still use all the regular React Native
packages.
- Expo doesn't technically lock you into anything, you can "eject" from Expo at
any time.
I decided to use Expo. While I have some concerns that Expo is an
investor-backed company and not a community effort, the Expo packages are all
open source. Expo Go is open source. And you can still install Android Studio
and XCode and build everything yourself.
## What else?
Expo comes with a lot out of the box, but there were some packages I wanted to
play with and some packages I knew I wanted on top of that. Some of these include:
### Tamagui
[Tamagui](https://tamagui.dev) is a UI kit for React Native. I first saw it
advertised as a "headless" library meaning that it implements all the
functionality but comes with no styles, so you style everything yourself. I
later found out that this was not entirely correct though because Tamagui does
come with default styles, but lets you opt out of them or customize them.
I really like Tamagui because it comes as a complete package. It's not just UI
components but also animations, CSS shorthands and a token based design system.
It also comes with a cool "sub-theme" system that allows you to have variants of
your themes, so you don't just have a "dark" theme but you can also have
"dark-forest" and "dark-blue" and "dark-whatever" to create different themes for
different sections of you app without having to hand-code everything.
### Expo-Sqlite
[Expo-sqlite](https://docs.expo.dev/versions/latest/sdk/sqlite/) is an expo package, but it's an optional one.
It gives you Sqlite. That's about all. Sqlite is awesome, so why not.
There are some other options for storage, some even support additional features like encryption
if that's something you need. But I don't think it's crucial if I encrypt
my sourdough starter data, so I'd rather take the Sqlite features.
### nearform/sql
[@nearform/sql](https://github.com/nearform/sql) is a library for SQL injection
prevention. What's cool about it is that you get to write SQL queries with the
javascript string interpolation without risking SQL injection. So you can do:
```js
DB.query(SQL`INSERT INTO students(name) VALUES (${name})`);
```
without little [Bobby Tables](https://xkcd.com/327/) ruining your day.
nearform/sql is not compatible with Expo-sqlite out of the box, but a basic
wrapper function can easily get you there. Here it is, I'd
publish it on npm but it's so short you might as well just copy and paste it.
```ts
export function sql(strings: any, ...values: any[]): Query {
const statement = SQL(strings, ...values);
return {
sql: statement.text,
args: statement.values,
};
}
```
### SWR
[SWR](https://swr.vercel.app) describes itself as "React Hooks for Data
Fetching". My little app is local only and doesn't need to fetch data from
anywhere, so why SWR? Well, SWR is great whenever you need to get data from
anywhere outside of your apps own state. In my case, I need to get data out of
SQLite and into my app, so I use SWR to fetch the data from the database.
The main use case for me is the caching and deduplication. SWR caches results
and won't fetch again if the same hook is called multiple times, so you can use
your hooks everywhere without having to worry about the same data being fetched
multiple times for no reason. You can also invalidate the cache whenever you
want, which will invalidate all users of the hook and fetch the data just once
to update everything.
You need some workarounds to get SWR working in React Native, but [official docs have you covered](https://swr.vercel.app/docs/advanced/react-native.en-US).
### Formik
I was hesitant and I dragged my feet the very first time I encountered
[Formik](https://formik.org), but after trying it once I actually fell in love
with it. It really does everything you need a form to do. The best part to me is
that you get to avoid having a million `useState`s in your form, and thanks to
the Formik context you can organize your code without drilling values and
setters down into components.
You have to do a bit of work for Formik to work in React Native, but the [official guide tells you what to do](https://formik.org/docs/guides/react-native)
so it's an easy change. Wrap that in a custom form input component and you can forget
the incompatibility exists at all.
## Some More Packages
This is getting really long (for my usual posts), so a few more quick mentions:
- `date-fns` is the definitive date & time library. It's incredible.
- `radash` is like lodash. I'm honestly not fully sure if it's better or worse, but I do keep coming back to it.
- `rrule` makes calculating recurring dates easy. You can serialize and deserialize your rrule's which I also appreciate.
- `ulidx` is a random ID generator. It's like nanoid, but at the cost of just a few more characters they are ordered by creation date.
- `zod` is my favorite schema tool. Never leave home without it.
## Fin
I'm about done with the basic functionality I wanted for this app, so I'm hoping
to have a version of this up on the Google Play store by the end of the week.
Oh and I still haven't made that sourdough starter,