DEV Community

Bigyan Thapa
Bigyan Thapa

Posted on • Edited on

Simple steps to consume AWS pre-signed url in Android

I have learned very useful information from articles published by several great contributors in the community. This is the first time I am trying to give something back to the community.
Recently I came across a simple requirement of consuming an AWS pre-signed url in android and displaying the json data in a list, e.g.

https://s3-us-east-1.amazonaws.com/something/foo_bar.json

In the beginning, it felt fairly simple; just another API consumption task. With this thought I started implementing the network, persistence and repository layer first.

For this example, I am using Room for database, Retrofit for network communication, Moshi for json adapter. The basic requirement is pretty straight forward: when app launches, get the data, store and display in the list. When I started the implementation, I under the impression that consuming this endpoint would be as simple of consuming any other RESTful api. But it turned out to be a bit tricky.
So, below are simple steps to implement this requirement:

Setting up Retrofit adapter

    object FooRetrofit {

      val conversionFactory: MoshiConverterFactory by lazy {
          MoshiConverterFactory.create()
      }

      val retrofit: Retrofit by lazy {
          Retrofit.Builder().apply {
              baseUrl("https://amazonaws.com")
              addConverterFactory(conversionFactory)
          }.build()
      }
   }

Notice that I have used a dummy url as baseUrl when build the Retrofit client. I will explain why I did this later in the article.

Setting up Repository

    class FooRepository {
        /* Dao and Service are provided here*/

        suspend fun getData(url: String) : List<Data> {
            val dataList = service.getData(url).data
            dao.insertAll()
            return dataList
        }
    }

The repository class will call the service class to fetch data, insert to local storage (Room) and return the list.

Setting up service/ endpoint

    interface FooService {
        @Get
        suspend fun getData(@Url url: String): FooResponse
    }

Now, we see that the getData() function in our service is expecting an url as argument. Earlier, when we setup the adapter, we set up with the dummy url. The reasoning behind that is, if we setup the adapter with the given url, i.e. https://s3-useast1.amazonaws.com/something/foo_bar.json , compiler will throw an exception, baseUrl should end with '/'. To overcome this issue, we setup the adapter with a dummy url that meets the requirement.

Setup calling class/ ViewModel

    class FooViewModel: ViewModel() {

       /* repository is provided here */

        init {
            launch { 
                fetchData()
            }
        }

        internal suspend fun fetchData() {
            val result = repository.getData(URL)
            // And this result is our json data. The fragment/ activity associated with displaying this data can observe this `LiveData` or `Channel` or `Flow` and when data is received then display.
        }

        companion object {
            const val URL = "https://s3-us-east-1.amazonaws.com/something/foo_bar.json"
        }
    }

The sample json data for this implementation is:

    {
      "user": {
      "name": "Foo Bar",
      "address1": "Moon",
      "address2": "Mars"  
      },
      "books": [
      {
       "isbn": "asdf4fjklm02454ac3",
        "author": "Alien 1",
        "category": "Fiction",
        "price": 100
      },
      {
       "isbn": "asdf4f234m02454ac3",
       "author": "Alien 2",
       "category": "Drama",
       "price": 75
     }
     ...
     ]
   }

This was the first time I came across this type of requirement. If we had to do two-way communication, I believe there would be a different setup. But for simply consuming a pre-signed url, these were the simple steps.
This is my first article. So, please feel free to provide feedback. I look forward to sharing more with the community in the days to come.

Thank you!

Top comments (0)