SpaceX Android App

(Created for Mindera’s app challenge, in December 2021)

Light Dark

API

Requirements

  • Use the SpaceX API;
  • Company section, populated by the API’s data, displaying:
    • “{company name} was founded by {founder name} in {year}. It has now {employees} employees, {launch sites} launch sites, and is valued at USD {valuation}”;
  • Rocket Launches section, populated by the API’s data, each item displaying:
    • Mission name;
    • Launch’s date and time;
    • Rocket name and type;
    • Days since/from launch;
    • Mission patch image;
    • Successful launch or not;
  • Filter rocket launches by:
    • Years
    • Launch status
  • Sort rocket launches (ascending and descending)

About

  • Language: Kotlin
  • Orientation: Portrait (landscape is not disabled, but not ideal)
  • Unit tests: 53 (all passing)
  • Light and Dark themes
  • Tested on Android API 30 emulator
  • Dependencies:

The app contains all the requested features. A single page, where the user can see 2 clearly defined sections: Company and Launches. Users may filter rocket launches by minimum launch year and launch success, as well as order results by launch date in ascending or descending order. Touching a rocket launch item from the list opens up a dialog where the user may use links to read its article, Wikipedia, or watch its video.

Usage

On the first half of the development I used TDD (Test-Driven Development), but as the days went by, I started worrying about not having enough time to implement everything (and felt like you guys could already have a sense of how I implement unit tests). That’s when I dropped TDD and just developed the rest of the features, leaving the missing unit tests to whatever remaining time I would have.

App Flow

I tried my best to follow the Clean Architecture and DDD (Domain-Driven Design) teachings. Below are simplified sequence diagrams of the user interactions within the app. PS: Please, don’t expect too much of it; I don’t know UML rules or “best practices”, but I still risk myself using it from time to time because I think it’s helpful.

  • Home page: GetInitialDataEvent

Occurs as soon as the Home page is loaded, requiring no action from the user.

enter image description here

enter image description here

  • Home page: RefreshEvent

Occurs when the user pulls down the list, triggering the RecyclerView’s “swipe to refresh” feature.

enter image description here

  • Home page: ApplyFiltersEvent

Pre-required action: open up the filter’s dialog by touching the top-right action bar button. Occurs when the user touches the “APPLY” button.

enter image description here

REST vs GraphQL

When I started developing the filter feature, I had to do some research about how to query the launches data. Besides the official REST API “/v4/launches/query” endpoint, I found a SpaceX GraphQL API, that I later found out to be a non-official API (should’ve read their readme file more carefully 😅).

As I’ve never worked with GraphQL, I figured it would be a great opportunity to learn a little bit. So, after implementing the filter using the REST API, I implemented it using the GraphQL API as well. But unfortunately, this non-official API does not provide a way to filter rocket launches by “launch years greater than”, and therefore had to switch back to the REST implementation. However, since it took me a lot of time and effort to implement, I decided to keep the GraphQL data source in the project.

If you wish to test the GraphQL implementation, you just have to replace one by the other in the dependency injection file.

There’s another important reason not to use the GraphQL API though… The following requests return no results (even though the REST API returns 1+):

  • Requests filtering by “launch_success”, for both values “true” and “false”
  • Requests filtering by “launch_year”, only when its value is year “2021”

You may verify it by running the following queries on https://api.spacex.land/graphql/:

{
  launches(
    limit: 3,
    offset: 0,
    find: {
      launch_year: "2021"
    },
    order: "asc",
    sort: "launch_date_utc"
  ) {
    id
    mission_name
    launch_year
    launch_success
  }
}

and

{
  launches(
    limit: 3,
    offset: 0,
    find: {
      launch_year: "2020",
      launch_success: "true"
    },
    order: "asc",
    sort: "launch_date_utc"
  ) {
    id
    mission_name
    launch_year
    launch_success
  }
}

launch_success: "true" enter image description here

Main Packages

I usually create packages with readability in mind. For example: /home/rocketlaunch/filter By doing that, let’s say I have to implement something on the filter of the rocket launches list of the home page. It’s right there on the package name. Inspired by 😱 architecture

  • com/mindera/rocketscience/home

    • /itself Everything related to (guess what 😅) home itself. This is the presentation layer of the “Home” component. “itself” is a naming convention I use when I want to highlight an intersection between one context and others. In this case, the intersection between Home/Company and Home/Rocket Launches. See image below.

    • /company Home intersection with the Company context.

    • /rocketlaunch Home intersection with the Rocket Launch context.

    • /rocketlaunch/filter Home’s rocket launch list filter feature. Has its own ViewModel, used by its BottomSheetDialogFragment.

    Contexts

  • com/mindera/rocketscience/company

    • /data/local
      • /sharedprefs
    • /data/remote
    • /domain
      • /model
      • /usecase
  • com/mindera/rocketscience/rocketlaunch

    • /data/local
      • /room
      • /sharedprefs
    • /data/remote
      • /spacex/graphql
      • /spacex/rest
    • /domain
      • /model
      • /usecase

To-do

  • Unit test the data sources;
  • UI tests;
  • Segregate the dependency injection modules. They are contained in a single file, but should be distributed across the appropriate packages;
  • Create custom view in order to not repeat the same set of XML tags on every line of the rocket launch list item;
  • Show loading indicator at bottom of the list as well, when paginating.
  • Landscape mode.

References

https://github.com/r-spacex/SpaceX-API/blob/master/docs/queries.md https://developer.android.com/kotlin/coroutines https://github.com/ReactiveX/RxJava https://developer.android.com/jetpack/androidx/releases/room https://developer.android.com/training/dependency-injection/hilt-android https://material.io/components/buttons/android https://www.apollographql.com/docs/kotlin/ https://graphql.org/learn/ https://site.mockito.org/ https://square.github.io/retrofit/ https://stackedit.io/

Developer

Matheus de Oliveira Castro (matheuscastro.dev {at} gmail {dot} com)

GitHub

View Github