Why Apps suck

by Christoph Bühler

October 2023

Ok since you followed the click-baity-ish title of this post, let me tell you why app development rocks. 😛

For some time now, we at smartive struggled with native app development. We’re the heroes of the web, and with a cool PWA, who needs apps anyways?

But our latest enterprise into the topic gave us hope that app development is not that bad. Let me show you why.

The bird to the rescue.

Flutter and Dart

Flutter is a development framework, based on the language “Dart”, created by Google in 2011. After the invention, Google desperately searched for a reason to actually use it. They went as far as implementing their Angular framework in Dart. Luckily, that idea died.

However, Flutter is written in? Yes. Dart. Hooray, they found a use case.

Flutter solves (or tries to solve) the one problem all app developers share: you don’t want create code for all the different platforms. Sharing code and logic is the key to the developers’ hearts. If you need to fiddle around with XCode, Android Studio, Visual Studio, Whatever-Linux-Uses-VIM-Studio, and all their respective build systems…

Happily, Flutter compiles to iOS, macOS, Linux, Windows, and Android. Technically, also to web, but it’s definitely not what I want to use for web development. Flutter does a splendid job when it comes to mobile / desktop environments. The abstraction is consistent and you barely touch the native systems. But if you need to, you have the possibility to do so. The packages on pub.dev are solid and a great many of them support all platforms. It is also possible to use native C libraries, such as pdfium, to render PDFs in your own app - like we did.

The Dart language itself has evolved to a multi-paradigm language with neat features. It compiles to native code and can also run on servers, but again: I’d rather use other languages and frameworks.

The code below creates an app that runs on all supported platforms. The result is rather boring (and ugly), but hey, at least we don’t need to mess around with native build systems and such.

dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: Center(
child: Text('Hello World')
));
}
}

Minimal - for real - runnable app in Flutter and Dart.

Libraries matter

The simple hello world code above is not really suitable for anything else than this click-bait-thing (thanks for reading though! much appreciated). However, there are many really useful packages which help you develop cool apps. The first app we made used “Stacked”. Stacked is a framework that incorporates the MVVM pattern in your app, making it better testable and separating the business from the UI logic.

Basic diagram of the MVVM pattern

We wanted to add immutability because we strongly believe that immutability can solve a great many problems that originate in side effects. Enter “freezed”. This package allows you to create immutable data structures so that you can use them in your views without having to fear changes from other services.

And while I still write about libraries: we learned much and - you guessed it - created our own: “FluorFlow”. Disclaimer: the name was generated by ChatGPT. Second disclaimer: it’s under heavy development and in “yolo-alpha-state™”.

dart
()
final class MyService {
void doSomething() {
print('Hello World');
}
}
()
final class MyView extends FluorFlowView<MyViewModel> {
final svc = locator<MyService>();
const MyView({super.key});
Widget builder(
BuildContext context,
MyViewModel viewModel,
Widget? child,
) => Center(
child: Text('Hello World'),
);
MyViewModel viewModelBuilder(BuildContext context) => MyViewModel();
}

An injected service with a route. This generates code that will setup the dependency injection as well as the router to heed these objects.

In contrast to Stacked, we do not support more than the basic MVVM stuff. We think that other packages are better suited to provide widgets and opinionated components. But: routing, dialogs, and dependency injection are supported and rely on the Dart build system to create everything you need to write a beautiful app.

Communication is Key

Not everything is directly linked to Flutter though. Whether we do web or app development, communicating with APIs is an important thing. That’s why we use:

OpenAPI v3 uses normal HTTP requests. A JSON specification provides all the information for you to automatically generate a communication client in your target language with the adequate generator package.

gRPC on the other hand is based on HTTP/2 and provides its own protocol and serialisation magic. The big advantage of gRPC is: you can generate the stub code for your servers and all communication logic for your clients. Type-safe. Nice. Also, the binary serialisation and usage of HTTP/2 make it fast as f*ck.

protobuf
syntax = "proto3";
package helloworld;
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}

Our advice: use either of these technologies since you’re not required to create all your communication services/clients by yourself.

And why do apps suck now?

Since you read until here (or you just scrolled down 🤷🏼‍♂️), you may still want to know why apps suck. In 2023, we’re still not able to deploy apps in an easy way to their respective app stores.

Unless you dance - barely naked, mind - under the fourth full-moon of the year around the oldest tree in your hometown, praising the serene highness of storefrontness, singing storellujah, submitting your firstborn - not your app - to Apple, Google, and Microsoft, you’re not able to submit any apps.

Seriously now, Apple has a pretty complex workflow where you need to create certificates, mobile provisioning profiles, as well as additional provisioning information if you want to use macOS as well, and so forth. Google makes it a little easier, you can create the so called “key store” locally and upload it to the Google Play Store where your app resides. Microsoft does a better job at that, because they just let you upload the compiled app and they sign it for you.

Nonetheless, for each store, you have complex workflows with each their challenges to overcome:

  • Each review process is different
  • Different formats and sizes of any pictures or screenshots you want to provide to your users
  • Native build systems are kind of unstable - they just fail after some time because something changed

But there is salvation. Flutter does a splendid job at upgrading major versions. We upgraded several majors and never had any major issues with the code we wrote. As for the builds and submitting workflows: use https://codemagic.io/. “codemagic” is a build system that allows you to build and submit your app for any platform you desire. With codemagic, we could reduce the pain of building locally and eliminate the classical “works on my machine” problem. Plus, codemagic provides a CLI tool inside their workflows that allows you to create the certificates, key stores, and whatnot, automatically. Setting up codemagic is possible with a clickable UI and a more advanced yaml file.

Our GitLab / codemagic workflow

We build the app(s) for all required platforms and then automatically deploy them to the AppStore, Google Play Store and Microsoft App Store, all in preview mode and fully automated.

So to summarise: App development really is fun. Building and deploying the damn things - is not. With Flutter, Dart, and codemagic, we were able to create good looking, well-performing apps, that are automatically deployed to their stores. With said base setup, we’re looking forward to create more stunning and advanced apps.

Previous post
Back to overview
Next post