pkamppur / mobile-sdk-comparison-2020

MIT License
5 stars 2 forks source link

Mobile App SDK Comparison 2020

Comparing the developer experience of mobile SDKs offering declarative UI.

Contents

What

This repo contains a simple mobile app written using three different mobile SDKs which have a declarative UI:

This allows comparing tooling, APIs, and the overall developer experience of the SDKs.

Why

I wanted to try these mobile SDKs that have a declarative UI, and see how it feels to create an app with each one of them. I was interested in diving deeper into the developer experience with these tools, languages and libraries.

SwiftUI is the new hotness for cross-platform UIs on Apple platforms. Flutter promises a single cross-platform codebase for mobile, desktop, and web. React Native is familiar to React developers and enables deployment to both iOS and Android.

I chose these SDKs because of their modern declarative UI and hopefully better developer experience than what I've been used to with UIKit and standard Android. I have a strong background in native iOS and Android, but I haven't done any large projects with these mobile app technologies.

I wanted to create a simple app with all these SDKs to learn them better and see what it's like to start a new project with these techs in 2020. What comes out of the box, how easy is it to set up a new project, what else is needed for a simple real world app?

The aim is to create an app that looks and feels pretty much native. I've checked the apps on iOS, because SwiftUI is iOS-only, even though React Native and Flutter support cross-platform development. Looking at doing cross-platform (Android and iOS) would also be interesting, as would be learning Jetpack Compose.

What Is Declarative UI

Declarative UI means I, as the programmer, declare how I want the UI to look, not how to build it. HTML is an example. Plain old HTML is just static. Declarative UIs start to shine, in my opinion, when things change and become dynamic. For example, in the old imperative way (UIKit), code has to keep references to view to update them. In declarative code, there's only the code that describes how the UI should look in each possible state. No more keeping track of views, no more multiple callbacks touching the same view and sometimes conflicting when timing is just right etc. Classes of problems just go away.

Below is a good example. What UI is shown depends on the state of the app. If games have been loaded, they're shown. If not, show spinner. No more "remove this view when loading ends, and then add these to the table view, oh and add the table view as well". Also notice there's not much layout specified here. SwiftUI has nice defaults, so the most common thing is the default, and thus the code can be very short.

    var body: some View {
        Group {
            if let games = games {
                LoadedGameList(games: games)
            } else {
                ProgressView()
            }
        }
    }

So in short, UI is a function of the app state (in a mathematical sense). Read more about declarative UI from Flutter documentation.

The App

The app is pretty simple, but typical mobile app. It has a list view and a detail view. The app shows BoardGameGeek's The Hotness list, and allows user to view details of each board game. I tried to make app functionality and code organization as similar as possible in all the apps.

Data is fetched from a custom proxy server sitting in front of BGG XML Api 2. The server converts XML to JSON so the mobile app doesn't have to deal with XML.

The app has these features:

Every complex mobile app has functionality like this, so I'm sampling a good portion of what it feels like to develop a real app with these technologies.

SwiftUI

List View Detail View

React Native

List View Detail View

Flutter

List View Detail View

Prerequisites for Running The Apps

You'll need to have these tools installed. I have the versions listed.

Please refer to the documentation of those tools on how to install them.

Running The Apps

Starting Server

You need the server running before launching the mobile apps. Server is made with Node.js and is configured to run in Docker. You can also run it locally, but I don't have instructions or configuration for that.

cd Server
docker build -t mobile-server-2020 .
docker run -e DEBUG=1 -e PORT=6000 -p 38651:6000 -d mobile-server-2020

Running SwiftUI App

Open SwiftUI/TheHotnessSwiftUI.xcodeproj and run the project (Cmd-R).

Running React Native App

cd ReactNative
npm install
npx pod-install
npx react-native run-ios
npx react-native start

Running Flutter App

Launch iPhone Simulator.

cd Flutter
flutter run

My Impressions

Overall I found all of these SDKs to be easy to set up and use to create the sample app. I think all of them were equally easy to create this app.

Using UI toolkit which uses a declarative UI feels really great. All SDKs have a great story there. Building the app and UI was pretty fast, a few hours per app. Once I had everything for the list view, building the detail view was fast.

Composability was great with all the SDKs. It was easy first build the UI as one big blob and then split it up for easier understandability and any possible reuse. This is much better than UIViewController or Activity/Fragment composition.

All SDKs had their own quirks with regards to how to get the layout to what I wanted. With experience I would think they're all capable, fast and easy to use.

I think I like SwiftUI the most: good defaults and nice syntax means there's not much to code write, and view modifiers are a nice way of tuning layout. Modifiers also apply to whole subtrees, which may be handy sometimes. The language (Swift) has nice features and I feel SwiftUI is a good library, albeit it seems to be somewhat deep and complex when looking at everything it offers. I mean, complex animations etc. seem to be possible, looking at examples on the Internet, but they don't seem to be so easy to write, for a novice. Simple things are mostly simple, however. Apps feel very native, but SwiftUI doesn't work on Android, so there's that.

Overall developer experience was best with React Native. Hot reload worked great and made development fast and fun, app looked ok (out of the box), code for layout structure wasn't too verbose, and styling & tuning look and layout was clean and easy to do with StyleSheet. TypeScript feels a nice, clean and modern language. React Native's API feels clean and simple.

Unfortunately Flutter wasn't that impressive to me. It's developer experience didn't feel better in any area than either of the other choices. Hot reload was much better than SwiftUI's, but not better than React Native's. Language and library weren't as good IMO. Overall Flutter seems good, but it just doesn't excite me at all. The two other SDKs feel interesting, but this is a very personal opinion.

I've included a sample view for each language below. It's the row view used to display a single game in the list. SwiftUI has the shortest code.

The technologies have their strengths and weaknesses, mostly relating to supporting (or not supporting) multiple platforms (with varying levels of grace). Also getting little details just right for each platform will be more difficult the further away the SDK is from the platform conceptually. So the question becomes, what kind of an app are you building?

SwiftUI

struct GameRow: View {
    let game: Game

    var body: some View {
        HStack {
            RoundedThumbnail(url: game.thumbnailUrl)
            VStack(alignment: .leading) {
                Text(game.name)
                Text(game.yearPublished)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            .padding(.trailing)
        }
    }
}

Good

Could be better

React Native

export const GameRow = ({game}: {game: Game}) => {
  return (
    <View style={styles.gameRow}>
      <Image style={styles.thumbnail} source={{uri: game.thumbnailUrl}} />
      <View style={styles.textContainer}>
        <Text style={styles.title}>{game.name}</Text>
        <Text style={styles.yearPublished}>{game.yearPublished}</Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  gameRow: {
    backgroundColor: Colors.white,
    padding: 10,
    flex: 1,
    flexDirection: 'row',
  },
  textContainer: {
    padding: 10,
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
  },
  thumbnail: {
    width: 80,
    height: 80,
    resizeMode: 'cover',
    borderRadius: 5,
    overflow: 'hidden',
  },
  title: {
    fontSize: 18,
  },
  yearPublished: {
    marginTop: 1,
    fontSize: 14,
    color: '#999',
  },
});

Good

Could be better

Flutter

class GameListRow extends StatelessWidget {
  GameListRow({Key key, this.game}) : super(key: key);

  final Game game;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(children: [
        ThumbnailView(thumbnailUrl: game.thumbnailUrl),
        Flexible(
            child: Container(
                margin: const EdgeInsets.only(left: 8),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('${game.name}',
                        style: Theme.of(context).textTheme.headline6),
                    Text('${game.yearPublished}',
                        style: Theme.of(context)
                            .textTheme
                            .bodyText2
                            .apply(color: Colors.grey))
                  ],
                )))
      ]),
      decoration: BoxDecoration(
        border: Border(
          bottom: BorderSide(width: 1.0, color: Colors.grey.shade300),
        ),
        color: Colors.transparent,
      ),
    );
  }
}

Good

Could be better

More Complex Things to Try

Adding more things could really stress these SDKs to see how easy they are for making polished mobile apps:

It's the little platform look & feel things that take a lot of time to create. Cross-platform toolkits usually make it harder to get all the details right.

Implementing these things would increase my knowledge in the SDKs and how to create full-featured, rich and polished apps with them.

Send Feedback

I'd like to hear your comments, improvement ideas, and other feedback. You can use GitHub Issues to suggest improvements. I can also be reached on Twitter: @pkamppur.