The practical guide – Part 4: Dependency injection with Hilt

Reading Time: 9 minutes

The practical guide – Part 1: Refactor android application to follow the MVP design pattern
The practical guide – Part 2: MVP -> MVVM
The practical guide – Part 3: Clean Architecture

Developing an application with clean architecture and design patterns is a path to success. But if we don’t know how to handle the dependencies that we created, this path is not going to be straightforward. It is important to understand what dependency injection is, and how to handle the dependencies properly, so we don’t end up with a mess. In the previous article, we created DependencyProvider object, where we provided the dependencies. Now, we will use the framework Hilt that will provide the dependencies for us. But let’s start with the basics:

Dependency Injection

Before going into dependency injection, let’s define what is dependency alone? When Class A uses some methods of Class B, we say that A is dependent on B. So we have dependency A -> B. Now, imagine that A creates an instance of B, so whenever we create A, we don’t need to supply B, because A automatically creates B for itself.

This is not good. Why? Mostly, because we cannot “inject” or provide other implementation of B. Why do we need other implementations of B? Well, the most common use case is for testing. If we want to test A, it will be very helpful for us to be able to provide mock or test implementation of B, instead of the real one. So, what is the fix? The fix is very obvious: instead of letting A create B, we pass B through the constructor/method/parameter of A. That means, whenever we want to create an instance of A, we must provide an instance of B first. So, we will have:

This little tweak that we just made, has a fancy name Inversion of control which is a general term of the other fancy term Dependency Injection. For the difference between these two terms, you can check this SO answer.

Too many dependencies

In the example above, A has only one dependency, and B doesn’t have any. What if B, has any dependencies, and that dependencies have their own dependencies and so on…? Then, when we want to create an instance of A, we will have to create all of those dependencies. And, if we use A in many places? You see where I am going, right?

How to fix this? One thing you can think of is by creating some class where you handle all the dependencies there (Like our DependencyProvider class). And, that is ok, you can do it by yourself. But, you can also use some framework that can help you.

Hilt

As you may suspect, Hilt is a library that helps us with handling the dependencies. It is a Google library, made specifically for Android. It is the most popular dependency injection library for Android development and is much easier to use than the more general Dagger 2 library.

Before going to the code, we have to check the architecture of the Hilt implementation, and the concept it uses. The three main concepts are:

  • Module – a class in which we provide the dependencies. Here we create methods that return the actual implementation of the dependency.
  • Component – an interface or an abstract class that connects dependencies from the Module and the class where we use those dependencies (In Dagger 2, we had to create these components, but Hilt creates most of them for us).
  • Scope – annotation which connects the lifecycle of the objects that we provide in the module, and the component’s lifecycle. (Hilt also has the most common scopes created for us)

There are a lot of other things that we have to learn, but we will do it with the implementation.

Implementing Hilt

First, we have to add the library to our project. In the project’s root build.gradle file, we have to add hilt-android-gradle-plugin:

buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
    }
}

Then, we have to apply the Gradle plugin and add the dependencies:

plugins {
  id 'kotlin-kapt'
  id 'dagger.hilt.android.plugin'
}

dependencies {
    implementation "com.google.dagger:hilt-android:2.38.1"
    kapt "com.google.dagger:hilt-compiler:2.38.1"
}

Next, we’ll annotate our application class with @HiltAndroidApp. If you don’t have an application class, create one (Don’t forget to add the class in the AndroidManifest file).

@HiltAndroidApp
class QuotesApp: Application() {
}

Once we annotate our application class, we can provide dependencies to other Android classes by annotating them with @AndroidEntryPoint. Hilt supports these Android classes: Application (by using @HiltAndroidApp), ViewModel (by using @HiltViewModel), Activity, Fragment, View, Service and BroadcastReceiver. So, in our case, we will annotate our ViewModel with @HiltViewModel and move the getQuotesUseCase property in the constructor:

@HiltViewModel
class MainViewModel @Inject constructor(private val getQuotesUseCase: GetQuotesUseCase) : ViewModel() {
…
}

Next, we will annotate our MainActivity with @AndroidEntryPoint and we will remove every usage of DependencyProvider.

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    ...
}

Now, we told Hilt, that it should provide us an instance of GetQuotesUseCase, but it doesn’t know how to. Because GetQuotesUseCase is our class, we can use constructor injection to tell Hilt how to create GetQuotesUseCase instances.

class GetQuotesUseCase @Inject constructor(private val quotesRepository: QuotesRepository) {
    ...
}

We can do this for every class that we want to inject: QuotesRepositoryImplementation, LocalDataSourceImplementation and RemoteDataSourceImplementation. This is cool, but we still haven’t told Hilt how to provide the interfaces (QuotesRepository, LocalDataSource, …).

Hilt modules

For interfaces or classes that we cannot constructor-inject (Classes from some outside library), we have to create a Hilt module, where we can tell Hilt how to provide instances for those classes. In our case, we will create a few modules: RepositoryModule, DataSourceModule, NetworkModule and DatabaseModule, where we will provide all of the dependencies that cannot be constructor-injected.

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule{
}

We have to annotate the class with @Module and we have to tell in which component this module will be installed. Hilt has created some components that we can use. Every component defines the scope of the dependencies provided by the module. We installed QuotesModule in SingletonComponent, which means that the dependencies in this module will be Singleton (they will be created only once per application). Here are the other scopes that Hilt supports:

Hilt componentInjector for
SingletonComponentApplication
ActivityRetainedComponentN/A
ViewModelComponentViewModel
ActivityComponentActivity
FragmentComponentFragment
ViewComponentView
ViewWithFragmentComponentView annotated with @WithFragmentBindings
ServiceComponentService

There are two ways to inject dependencies in the module. With @Binds and with @Provides. When using @Binds, we tell Hilt which interface we want to return in the return type of the function, and as a parameter of the function we specify which implementation of the interface we want to provide.

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    abstract fun bindQuotesRepository(impl: QuotesRepositoryImplementation): QuotesRepository
}

@Module
@InstallIn(SingletonComponent::class)
abstract class DataSourceModule {
    @Binds
    abstract fun bindLocalDataSource(impl: LocalDataSourceImplementation): LocalDataSource

    @Binds
    abstract fun bindRemoteDataSource(impl: RemoteDataSourceImplementation): RemoteDataSource
}

The second way is with @Provides. A function annotated with @Provides supplies the following information for Hilt:

  • The function return type tells Hilt what type the function provides instances of.
  • The function parameters tell Hilt the dependencies of the corresponding type.
  • The function body tells Hilt how to provide an instance of the corresponding type. Hilt executes the function body every time it needs to provide an instance of that type.
@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
    @Provides
    @Singleton
    fun provideQuotesDao(quoteDatabase: QuoteDatabase): QuoteDao {
        return quoteDatabase.quoteDao()
    }

    @Provides
    @Singleton
    fun provideQuoteDatabase(@ApplicationContext context: Context): QuoteDatabase {
        return Room.databaseBuilder(
            context,
            QuoteDatabase::class.java,
            "quotes_db"
        ).build()
    }
}

@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
    @Provides
    fun provideQuotesApi(): QuotesApi {
        return RetrofitClient.getRetrofit().create(QuotesApi::class.java)
    }
}

In provideQuotesDao() we are asking for QuoteDatabase and with that, we can create our dao instance. Because we cannot construct-inject QuoteDatabase, we have to provide it too. For that, we need the application context. We can ask to get it as a parameter annotated with the Qualifier @ApplicationContext. Let’s see what qualifiers are:

Qualifiers

In some cases, we need multiple bindings for the same type. For instance, we might need two bindings for the retrofit client. One with authentication and another without. In order to implement it, we need to create two qualifiers:

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthenticatedRetrofitClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class NotAuthenticatedRetrofitClient

And then, we will have to annotate the binding where we bind/provide it, and where we inject (use) it. We’ll have to change our NetworkModule:

@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
    @Provides
    @Singleton
    fun provideQuotesApi(@NotAuthenticatedRetrofitClient retrofit: Retrofit): QuotesApi {
        return retrofit.create(QuotesApi::class.java)
    }

    @Provides
    @Singleton
    @NotAuthenticatedRetrofitClient
    fun provideNotAuthenticatedRetrofitClient(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://programming-quotes-api.herokuapp.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

We don’t use authenticated retrofit clients in our case, but it is a nice example. There are some predefined qualifiers provided by Hilt, one example is the @ApplicationContext that we used, there is also @ActivityContext and you can find some more here.

One last thing that I want to mention is the Component Scopes. By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type. Hilt allows us to scope a binding to a particular component. This means that the same instance of the binding will be used during the lifetime of the component. For every component that Hilt has predefined, it also has a scope for it. For instance, SingletonComponent has @Singleton scope, ActivityComponent has @ActivityScoped etc…

For our application, that’s it. We can get rid of the DependencyProvider file and we have successfully implemented Dagger Hilt. You can check out the code here.

Elevate @AssistedInject to new heights in your Android project

Reading Time: 4 minutes

Everybody used a form of DI in their projects, Android developers use(d) Dagger2/Hilt at least once in their careers.

Throughout this experience, your life was simplified and just one @Inject saved you from writing boilerplate code. This wasn’t always the case, you needed to add something dynamically in one of your dependencies, this is where @AssitedInject comes into play.

An assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in, at creation time (a.k.a “assisted”), by the developer (you).

The assisted injection uses a factory to provide your assisted dependency, the steps are as follows:

  1. Annotate your dependency with @AssitedInject
  2. Provide the dependencies that can be automatically wired by the DI library
  3. Annotate your dynamically added dependencies with @Assisted and provide them with a name if needed
  4. Create a factory for your dependency annotated with @AssistedFactory and a function that creates and returns your assisted dependency

To facilitate the aforementioned steps, in this blog post you’ll now build a reusable one shot shared preferences dependency.

In order to have our “OneTimePreference”, we create a common contract so that each dependency that implements it will behave as agreed.

interface OneTimePrefContract {
    val isOneTimeShown: Boolean
    fun setOneTimeShown()
    val oneTimePrefs: SharedPreferences
}

The real implementation comes in a form of an “assisted” dependency that implements the contract and is provided from a factory.

class OneTimePref @AssistedInject constructor(
    @ApplicationContext private val context: Context,
    @Assisted(PREFS_TAG_KEY) private val prefsTag: String,
    @Assisted(PREFS_BOOLEAN_KEY) private val prefsBooleanKey: String
) : OneTimePrefContract {
    
    private companion object {
        private const val PREFS_TAG_KEY = "prefsTag"
        private const val PREFS_BOOLEAN_KEY = "prefsBoolean"
    }
    
    @AssistedFactory
    interface OneTimePrefFactory {
        fun create(
            @Assisted(PREFS_TAG_KEY) prefsTag: String,
            @Assisted(PREFS_BOOLEAN_KEY) prefsBooleanKey: String
        ): OneTimePref
    }
    
    override val oneTimePrefs: SharedPreferences
        get() = context.getSharedPreferences(
            prefsTag,
            Context.MODE_PRIVATE
        )
    override val isOneTimeShown get() = oneTimePrefs.getBoolean(prefsBooleanKey, false)
    override fun setOneTimeShown() = oneTimePrefs.edit { putBoolean(prefsBooleanKey, true) }
}

As you see the factory provides the same assisted parameters that are needed in order for the assisted inject to happen while having an external dependency from the outside as with our application context.

You can now reuse it anywhere.

@AndroidEntryPoint
class WalkThroughFragment : Fragment (){
	
	@Inject
	lateinit var oneTimePrefFactory : OneTimePref.OneTimePrefFactory

	private val walkThroughPreferences : OneTimePref by lazy {
		oneTimePrefFactory.create("walkthrough-prefs", "walkthrough-isShown") // consider using constants, this is for demonstration purposes only
	}
}

Congratulations, you’ve learned @AssistedInject

There is one limitation by the DI framework and one big issue with this code.

  • @AssistedInject dependencies can’t be scoped
  • This is a lot of boilerplate to write

In order to write less boilerplate Kotlin’s delegation is one hell of a powerful tool to know and we want our dependency to be scoped to the lifecycle of a Fragment (for demonstration purposes).

@FragmentScoped
class WalkThroughPrefsProvider @Inject constructor(
    private val oneTimePrefFactory: OneTimePref.OneTimePrefFactory
) : OneTimePrefContract by oneTimePrefFactory.create(
    WALK_THROUGH_PREFS,
    WALK_THROUGH_PREFS_SHOWN_KEY
) {
    
    private companion object {
        private const val WALK_THROUGH_PREFS = "walkThrough"
        private const val WALK_THROUGH_PREFS_SHOWN_KEY = "walkThroughKey"
    }
}

Now you can go around injecting your WalkThroughPrefsProvider and having more readable code.

@AndroidEntryPoint
class WalkThroughFragment : Fragment (){
	
	@Inject
	lateinit var oneTimePrefFactory : WalkThroughPrefsProvider
}

The code is publicly available as a Gist.

Bundletool and how to utilize Android App Bundle

Reading Time: 6 minutes

The App Bundle is the new official publishing format that app developers use to publish their apps on Google Play as opposed to APK, the traditional way that was done for over 10 years of android’s history.

Splitting the Publishing from the Serving format

In the past, app developers uploaded monolithic APKs to Google Play, and the channel acted like a dumb pipe that did the distribution to the users.

But with the App Bundle, a conscious decision was made to split the publishing format and the serving format. Developers now put everything in this publishing format, and then Google Play processes it and generates optimised APKs to serve the best possible APK to the end device.

The contents of an Android App Bundle with one base module, two dynamic feature modules, and two asset packs.
(source android-developers)

Testing Android App Bundle

This is where the bundletool comes in. It is actually the underlying tool that Google Play and the Android Gradle plugin use to build .aab files. This tool is also available as a command-line tool so that developers can test their app locally and emulate Play server-side build and test out Play App Delivery or Asset Delivery flows, before uploading the artefact.

The tool has a few different responsibilities that help developers manipulate Android App Bundles:

  • Build an Android App Bundle from pre-compiled modules of a project.
  • Generate an APK Set archive containing APKs for all possible devices.
  • Extract APK(s) from the APK Set compatible with a given device.
  • Install APK(s) from the APK Set compatible with a connected device.
  • Extract device spec from a device as a JSON file.
  • Add code transparency to an Android App Bundle. Code transparency is an optional code signing mechanism.
  • Verify code transparency inside an Android App Bundle, APK files or an application installed on a connected device.

How to build APK sets from App Bundle

The command build-apks is used to build an APK set for the bundle. It will contain all the APKs for all the modules in the project.

bundletool build-apks 
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks

If the APKs were installed on a device, they would need to be signed with a private key, all the APKs contained in the APK set will be signed and installable. Note, if signing information is not specified, bundletool will attempt to sign the APK with a debug key.

bundletool build-apks 
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks
--ks=/ExampleApp/keystore.jks
--ks-pass=file:/ExampleApp/keystore.pwd
--ks-key-alias=ExampleKeyAlias
--key-pass=file:/ExampleApp/key.pwd

Generating APK sets for devices

You can target a device by specifying its configuration in a JSON file. This file can be created manually with specifying information about supported Application Binary Interfaces (ABIs), Locales, Device Features, OpenGL Implementations, Device Screen Density and the SDK version. Or just simply delegate this work to the useful command of bundletool, get-device-spec.

{
  "supportedAbis": ["arm64-v8a", "armeabi-v7a", "armeabi"],
  "supportedLocales": ["en-US", "de-DE", "mk-MK"],
  "deviceFeatures": ["android.hardware.bluetooth","android.hardware.camera",    "android.hardware.microphone","android.hardware.nfc"...],
  "glExtensions": ["GL_OES_EGL_image","GL_OES_EGL_image_external","GL_OES_EGL_sync"...],
  "screenDensity": 440,
  "sdkVersion": 30
}                                                                      pixel4a.json
bundletool build-apks 
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks
--device-spec=pixel4a.json

When passing the flag connected-device, bundletool will create APKs just for the device currently connected to, or serial ids can be specified with the command device-id when multiple devices are connected.

bundletool build-apks 
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks
--connected-device

If device flag attributes are omitted when building the APKs, the bundletool will generate an APK set that will contain APKs for all possible devices.

Universal APK

The bundletool can also create a universal APK by passing the universal flag. This APK will contain all the files for all the device configurations. Because of that, it can be installed on any device. This will not by any means represent what the user will get when the app is installed from Play Store, however, this is a convenient way to pass an APK to users when you don’t know what device they are using.

bundletool build-apks 
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks
--ks=/ExampleApp/keystore.jks
--ks-pass=file:/ExampleApp/keystore.pwd
--ks-key-alias=ExampleKeyAlias
--key-pass=file:/ExampleApp/key.pwd
--universal

Testing Dynamic Feature Modules installation

When testing feature modules while building a universal APK, bundletool will include only the modules that specify <dist:fusing dist:include="true"/> in their manifest. If an attribute is set to false the definition of the activities will still be merged in the AndroidManifest.xml of the base module, however, the DEX files, Resources, Assets and Native libraries will not be.

A more convenient way of trying out the dynamic feature modules is by adding a local-testing argument to the build-apks command, which adds special metadata that will let us test feature module installation locally.

bundletool build-apks
--local-testing
--bundle=/ExampleApp/example_app.aab 
--output=/ExampleApp/example_app.apks

Deploying APKs to a connected device or an emulator

After a set of APKs are created, bundletool has the ability to extract the right combination of APKs from that set to a connected device or an emulator, by using the command install-apks.

Note, when using the local testing flag, bundletool will read the metadata from the previous step and push all optional modules into the device’s local storage.

bundletool install-apks 
--apks example_app.apks

Get APK size from an APK set

By providing the path of the APKs and adding the command get-size total, bundletool can measure the estimated download sizes as they would be server compressed over-the-wire. Adding the modules flag would provide the exact size of the base module plus the specified module or modules.

bundletool get-size total 
--apks=/ExampleApp/example_app.apks
--modules=module1

Support for Code Transparency for App Bundles

As of version 1.7.0 bundletool has added support for adding and checking transparency, which ensures the app’s integrity. To be precise SHA256 hashes are created for each DEX file and each .so file that is part of the App Bundle. Along with a singed code transparency file, the developer can verify the matching hashes using a singing key that is private to the developer. About how code transparency works and in-depth analysis on how to use bundletool while adding, verifying an App Bundle or APK set, you can read more here.

Android App Bundle is Open Sourced through bundletool

With APK sizes going as light as possible, and functionalities like on-demand feature installations, the future of the bundle format is looking bright in the Android Ecosystem.

With open-sourcing bundletool Google has indirectly open-sourced the App Bundle, meaning if any other distribution channels want to get in the action and implement support for bundles, they could with the help of bundletool.


This tool has given app developers the means to manipulate the App Bundle. If you have not tried it yet, download this command-line tool from the Github repository and give it a go. Cheers.

The practical guide – Part 3: Clean Architecture

Reading Time: 7 minutes

The practical guide – Part 1: Refactor android application to follow the MVP design pattern
The practical guide – Part 2: MVP -> MVVM

We know what design patterns are and how to implement them. But, if we want to have a more robust, scalable, maintainable, and testable application we have to do more. Today we will learn how to implement Clean Architecture proposed by Robert C. Martin (Uncle Bob).

Why is Architecture important?

Architecture is important for managing the complexity of the project. So, for a small project, we may not need one, but for big ones, it is a lifesaver. It makes the project easier to maintain, scale and test. It also makes the project more organized, so everyone can understand what the project is about with only looking at the classes.

Clean Architecture Introduction

Design patterns told us how to separate presentation and manipulation with the data. A clean architecture (like any other architecture) goes a little deeper and shows us how we should separate the manipulation of the data. Clean architecture has only a few layers, and each layer has its own responsibilities.

All credit for the image goes to Uncle Bob.

You have probably seen this image. It tells us how the layers are organized. As we can see, there are outer layers and inner layers. That is important because there are few rules that we have to follow:

  • Every layer can communicate only with the inner layers. So, the layers don’t know anything about the outer layers. The dependencies are provided by outer layers with Dependency Injection (hopefully I will make a post about this).
  • The most inner layer is the most abstract, and the most outer layer is the most concrete. This means that inner layers should contain business logic and outer layers should contain the implementation.

You may have noticed that I mentioned a few layers and not an exact number. That is because the clean architecture doesn’t define an exact number of layers. You can adapt the number of layers to your needs.

The flow

  • View The responsibility for the view stays the same as specified in the design patterns. Its only responsibilities are to display the data and react to user actions.
  • View Model/Presenter – Also, specified in the design patterns, their responsibility is to pass the data between the view and the model. But, instead of making the network/database calls, it uses the Use Cases for it. So, these classes shouldn’t know, where the data comes from, or where it goes. It just passes the data between the Use Cases and the Views.
  • Use Case (or Interactor) – These are the actions that the users can trigger. It contains the data that the action needs for it to be completed, and calls the repository to do the action. It can decide on which thread the action should be done, and on which thread the result should be posted.
  • Repository – The responsibility of the repository is to decide which data source the data needs to be handled. For every Use Case, there should be a method in the repository.
  • Data Source – There may be few data sources per application, like Network, Database, Cache etc. It contains the actual implementation of the data source.

Between every layer, we can have Mapper classes since the data can differ between layers. For example, we would like to store different data in a database from the one that we get from the network.

The implementation

Let’s start with the data sources. We will create two data source interfaces: RemoteDataSource and LocalDataSource.

interface RemoteDataSource {
  fun getQuotes(): Call<List<Quote>>
}

class RemoteDataSourceImplementation(private val api: QuotesApi) : RemoteDataSource {
  override fun getQuotes(): Call<List<Quote>> = api.quotes
}
interface LocalDataSource {
  fun insertQuotes(quotes: List<Quote>)
  fun getQuotes(): List<Quote>
}

class LocalDataSourceImplementation(private val quoteDao: QuoteDao) : LocalDataSource {
  override fun insertQuotes(quotes: List<Quote>) {
    quoteDao.insertQuotes(quotes)
  }

  override fun getQuotes(): List<Quote> = quoteDao.allQuotes
}

As you can see, we added only one method in RemoteDataSource, just for getting the quotes, but there are two methods in LocalDataSource since we have to store the quotes to the database after getting them from remote. The very important thing here is to notice that we are not creating the DAO and API objects, but we are asking for them to be provided in the constructor (Dependency Injection). This will enable us easily to switch to different network or database libraries and we won’t have to change anything here.

Let’s continue with the repository. We said that its responsibility is to decide where the data will come from. In our case, we want to return the quotes from the network, but if something fails we want to display the quotes from the database.

interface QuotesRepository {
  fun getQuotes(): List<Quote>
}

class QuotesRepositoryImplementation(
  private val localDataSource: LocalDataSource,
  private val remoteDataSource: RemoteDataSource
) : QuotesRepository {
  override fun getQuotes(): List<Quote> {
    return try {
      val response = remoteDataSource.getQuotes().execute()
      if (response.isSuccessful) {
        val quotes = response.body() ?: return localDataSource.getQuotes()
        localDataSource.insertQuotes(quotes)
        return quotes
      }
      localDataSource.getQuotes()
    } catch (e: Exception) {
      localDataSource.getQuotes()
    }
  }
}

It is a dumb logic, but I made it like that just for simplicity. Also very important is that we are using the interfaces, and not the actual implementation.

Let’s move to the use case. Here we will use the repository and we will switch between threads. We will use Kotlin coroutines, but you can use anything. If you are working with RxJava, here you will specify the Schedulers.

class GetQuotesUseCase(private val quotesRepository: QuotesRepository) {
  fun getQuotes(onResult: (List<Quote>) -> Unit = {}) {
    GlobalScope.launch(Dispatchers.IO) {
      val response = quotesRepository.getQuotes()
      GlobalScope.launch(Dispatchers.Main.immediate) {
        onResult(response)
      }
    }
  }
}

Usually, there is a base UseCase class, where you abstract the logic for threading. For simplicity, I skipped the error handling.

Last, we can clean up the ViewModel. I also converted it to Kotlin, and now it looks like this:

class MainViewModel : ViewModel() {
  lateinit var getQuotesUseCase: GetQuotesUseCase

  var quotesLiveData = MutableLiveData<List<Quote>>()

  fun getAllQuotes() {
    getQuotesUseCase.getQuotes { quotes: List<Quote> ->
      quotesLiveData.postValue(quotes)
    }
  }
}

I won’t explain anything here, I will just let you admire. Just kidding! You must be asking how we create getQuotesUseCase. But that is a story for another time. For now, I will create a class DependencyProvider, and I will provide everything there. You don’t have to worry about this for now.

And that’s it. Now we have an application that follows Clean Architecture guidelines. Here is the link for the project.

Final Notes

Now that our project follows Clean Architecture guidelines we can do many things. We can easily change frameworks and libraries with as little changes as possible (just changes at the DependencyProvider and everything will work). We can organize the application better with many repositories and many data sources. The project will be easy to understand, just by looking at the use cases. Testing of the application will be very easy (hopefully I will make a post about that, too).

I hope that this post will help you understand how Clean Architecture works in practice. If you have any questions or need any help don’t hesitate to ask. Happy Coding!

The practical guide – Part 2: MVP -> MVVM

Reading Time: 5 minutes

The practical guide – Part 1: Refactor android application to follow the MVP design pattern

This is the second part of the practical guide. In the first part, we talked about refactoring android application to MVP. Now, instead of refactoring the base code to MVVM, I would like to refactor the MVP application to MVVM. That way we will learn the differences between MVP and MVVM.

Why MVVM?
First, I should tell you that Google accepted MVVM as preferred design pattern for building Android applications. So, they have build tools that helps us following this pattern. This is a great reason to learn and use this pattern, but why would Google choose MVVM over MVP (or other design patterns). Well, they know best but, my opinion is that they chose MVVM because it has one less dependency, due to the difference in communication between ViewModel and View. I will explain this in the next section.

Difference between MVP and MVVM
As we know, MVP stands for Model-View-Presenter. On the other hand, MVVM stands for Model-View-ViewModel. So, the Model and the View in MVVM are the same as in the MVP pattern. The only difference remains between the Presenter and View Model. More precisely, the difference is in the communication between the View and the ViewModel/Presenter.

As you can see in the diagrams, the only difference is the arrow from Presenter to View. What does it mean? It means that in MVP you have an instance of the Presenter in the View, and you have an instance of the View in the Presenter, hence double arrow in the Diagram. In MVVM you only have an instance of the ViewModel in the View. But, how do you communicate with the View? How can the View know when the ViewModel has made changes in the Model? For that, we need the Observer pattern. We have observables(subjects) in the ViewModel, and the View subscribes to this observables. So, when the observable is changed, then the View is automatically informed about that change, and it can update its views.

For practical implementation of this Observer pattern, we have to get help either from some external libraries like RxJava, or we can use the new architecture components from Android. We will use this later in this example.

Refactoring

MVP classes
MVVM Classes

First, we can get rid of the MainPresenter and MainView interfaces. We can have only one new class MainViewModel, that replaces the Presenter. Then we can extend MainViewModel from androidx.lifecycle.ViewModel. This is the first class that we will use from the android lifecycle components. This class helps us to deal with the lifecycle of the view model. It survives configuration changes, so it is a nice place for storing UI related data. Next, we will add quoteDao and quotesApi fields. We will initialize them with setters, instead of the constructor, because the creation of the ViewModel is a little bit different. We don’t have the MainView, and also we don’t need bindView() and dropView() methods.

Next, we will create the observable objects. These are objects that we want to display in the MainActivity, wrapped with androidx.lifecycle.LiveData or some other implementation of LiveData. This class helps us with the implementation of the observer pattern. We will create the objects in the MainViewModel, and we will observe these objects in the MainActivity. We want to display a list of Quote objects, so we will create MutableLiveData<List<Quote>> object. MutableLiveData because we want to change the value of the object manually.

getAllQuotes() will be very similar as in the Presenter, except minus the interaction with the MainView. So, instead of:

if (view != null) {
  view.displayQuotes(response.body());
}

we will have:

quotesLiveData.setValue(response.body());

We will also change the implementation of the DatabaseQuotesAsyncTask, so instead of sending the MainView, we will create a new interface that will get us the quotes from the async task and we will send the implementation of this interface there. In the implementation, we will update quotesLiveData, same as above.

In the MainActivity, we remove the implementation of the MainView. We won’t need to override onDestroy() method. We can replace the MainPresenter with the MainViewModel. We will create the ViewModel as follows:

viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);
viewModel.setQuoteDao(quoteDao);
viewModel.setQuotesApi(quotesApi);

Then we will observe to the quotesLiveData observable, and display the list.

viewModel.quotesLiveData.observe(this, quotes -> {
    QuotesAdapter adapter = new QuotesAdapter(quotes);
    rvQuotes.setAdapter(adapter);
});

And in the end, we call viewModel.getAllQuotes() to fetch the quotes.

And that’s it! Now we have an application that follows the MVVM design pattern. You can check the full code here.

4 steps to start building apps with Flutter

Reading Time: 2 minutes

As a front-end developer, I was always curious about mobile apps and wanted to build one. In the last years, I was testing multiple frameworks from Ionic to React native and to be honest, I was never satisfied. Until one day by accident, I tried FLUTTER and this happened:

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobileweb, and desktop from a single codebase.

From Flutter website

Just reading this sentence blew my mind and I was in. After two months playing around with the framework, I would say it’s the one that will take over in the next years for sure. Let’s jump and see how to start with it.

1 – Download the Flutter SDK

Download the stable version and put it in as a PATH environment variable. The download link is here.

2 – Run Flutter Doctor

flutter doctor

This command is the most important one as it checks your environment and displays a report of the status of your Flutter installation. Do not forget to check the output carefully, to be able to know what is still missing.

3 – Start Coding

import 'package:flutter/material.dart';

void main() async {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: MyApp(),
      ),
    ),
  );
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Text(
           'Click me!',
           style: TextStyle(
                  fontSize: 60.0,
                  fontWeight: FontWeight.bold,
                ),
           );
  }
}

4 – Enjoy it

What is White Labeling in Software Development? How to implement it for Android?

Reading Time: 3 minutes

What is White Label?

A white-labelled product is basically a software product or a service that is developed or created by some company and other companies can buy and rebrand it to their need so the users of this product don’t know about the real creator of the product but the brand. To explain it better, assume that there is a white-label company that makes an app and sells it to companies A and B and then they rename the app name to A and B and change the content of the app corresponding to their products so then the application becomes the service of companies A and B.

Why to use White Label product?

White label products become handy in certain situations. Especially it is better to go with a white-label solution when you want to enter a market with a minimum cost and time. It might be in case you want to start a startup project and don’t want to invest much in the beginning then the white-label solution is a good choice. Some advantages and disadvantages of using a white-label solution are listed below:

Advantages:

  • Less time to market time
  • Cost-efficient (time, money)
  • No developer hiring needed

Disadvantages:

  • Fewer customization capabilities
  • No control over the quality of the software
  • Dependency on external sources (developer)

Important points to consider

There are some important aspects which should be considered by a company which makes a white-label solution and by a client of this product.

  • Technical documentation – complete technical documentation of the software depending on the agreement between both sides.
  • Scope of customization – both sides must know which parts of the product can be customized, what kind of new functionalities can be added, what kind of limitations might occur or etc.
  • Maintenance & Support – for how long and what kind of maintenance and support should a client expect.

A simple explanation of how to implement a White Label for Android application

In Android, it is simple to do white label implementation thanks to “productFlavors” and “flavorDimensions“. By means of these two terms, it is possible to have different resources for different applications such as different themes, colours, logos, application names and etc. Additionally, using the” gradle” file we can also create some configurations to enable or disable certain functionalities of the app based on the needs of the customer. At the end when we build the application, only the resources which belong to the selected flavour and dimension will be included in the apk file.

Conclusion

To conclude the blogpost I would emphasize the two reasons which I think the most important reasons to use a white label product for your projects. The first one is it would cost you less financial investment (saving money). The second is less time to market time (quick launch) since you don’t need to do everything from scratch. These reasons sound good but it is better you always do your own analysis and comparisons before you decide what is best for your scenario.

Thanks for reading!

Below I have listed some links which I think is worth checking if you are going to implement a white label for an Android project:

The practical guide: Refactor android application to follow the MVP design pattern

Reading Time: 8 minutes

At the beginning of my carrier, I was very lucky to start a few new projects at my company. And every story was a short one and the same: This is going to be the best project ever -> Ops, it got little messy -> Oooops, everything is a mess, there is no going back. So, I realized that it isn’t enough that I can do anything the PO wants, but I have to find the right way of doing it.

I was thinking like: Ok, I will learn unit testing, my code will be tested, and everything will be fine. So, I got into testing, started to read everything about testing, especially unit testing. I started to understand it a little bit, so I decided to write a few unit tests in my existing (mess) projects. I created test classes for some fragment, started to type some test methods, thinking I will figure it out what to test easily, and when I had to type the name of the test method my mind froze.

I had no idea what to test. I started to divide the code into methods, but it wasn’t helping at all. After a ton of research and frustration, I realized that I can’t test anything until I learn some design patterns and/or architectures.

Design patterns and architectures

In theory, there is a small difference between design patterns and architectures. Basically, they are both terms that define the organization of code, but the difference is the layer. Architecture is a more general term of design pattern. There are many design patterns and architectures, and you can’t say for one that it is the best. Today we will look at the MVP design pattern, and we will try to refactor a really bad code into a better one. Hopefully, then, we will try to refactor the better code into an even better one, with clean-like architecture.

Application example

I created an application that fetches some programming quotes, saves them into a room database and displays them into a RecyclerView. If the network calls fail, we display the quotes from the database. It is a common use case, that complicates our lives a little bit. You can check out the no_pattern branch, and run the app.

public class MainActivity extends AppCompatActivity {

    private QuoteDao quoteDao;
    private RecyclerView rvQuotes;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        quoteDao = QuoteDatabase.getQuoteDatabase(this).quoteDao();

        rvQuotes = findViewById(R.id.rvQuotes);
        rvQuotes.setLayoutManager(new LinearLayoutManager(this));

        QuotesApi quotesApi = RetrofitClient.getRetrofit().create(QuotesApi.class);
        Call<List<Quote>> quotesCall = quotesApi.getQuotes();
        quotesCall.enqueue(new Callback<List<Quote>>() {
            @Override
            public void onResponse(Call<List<Quote>> call, Response<List<Quote>> response) {
                if (response.isSuccessful()) {

                    new Thread(() -> quoteDao.insertQuotes(response.body())).start();

                    QuotesAdapter adapter = new QuotesAdapter(response.body());
                    rvQuotes.setAdapter(adapter);
                }
            }

            @Override
            public void onFailure(Call<List<Quote>> call, Throwable t) {
                DatabaseQuotesAsyncTask asyncTask = new DatabaseQuotesAsyncTask(quoteDao, rvQuotes);
                asyncTask.execute();
            }
        });
    }

    static class DatabaseQuotesAsyncTask extends AsyncTask<Void, Void, List<Quote>> {

        private QuoteDao quoteDao;
        private WeakReference<RecyclerView> rvQuotes;

        public DatabaseQuotesAsyncTask(QuoteDao quoteDao, RecyclerView rvQuotes) {
            this.quoteDao = quoteDao;
            this.rvQuotes = new WeakReference<>(rvQuotes);
        }

        @Override
        protected List<Quote> doInBackground(Void... voids) {
            return quoteDao.getAllQuotes();
        }

        @Override
        protected void onPostExecute(List<Quote> quotes) {
            QuotesAdapter adapter = new QuotesAdapter(quotes);
            rvQuotes.get().setAdapter(adapter);
        }
    }
}

This code is terrible. I mean, it is doing its job, but it is not testable, it is not scalable, it is not maintainable. We can’t test this code. MainActivity is 82 lines of code, just for displaying a list of quotes. Imagine if we add more calls here, more features in this screen (and usually there are more features), this code will easily become a mess.

How to fix this? We will start with a design pattern. We will refactor this code to follow the MVP pattern. Now, what is the MVP design pattern and how to implement it? MVP pattern is one of the most common design patterns. It is very close to MVC and MVVM. All of these design patterns (and others) share the idea that we should define and divide the responsibility of the classes. All of the mentioned design patterns above have 3 types of classes:

  • Model – data layer, used for managing business model classes
  • View – UI layer, used for displaying the data
  • Controller/Presenter/ViewModel – logic layer, intercepts the actions from the UI layer, updates the data and tells the UI layer to update itself

As you can see, Model and View classes are the same between all three patterns (they may have different responsibilities), the difference is the remaining class.

Let’s talk about MVP strictly, and try to convert our app to follow the MVP pattern. What classes belong to which type?

  • Model – Here belongs just the Quote class, so it stays the same
  • View – Here belong Activities and Fragments. In our case MainActivity
  • Presenter – We should create presenter for every Activity/Fragment

So, in the data layer (Model) we have just our Quote class and it stays the same. The View and the Presenter are left. Let’s create interfaces for the Views and Presenters. That way we will create the communication between them.

public interface BaseView<P> {
}

public interface BasePresenter<V> {
    void bindView(V view);
    void dropView();
}

Every Activity/Fragment should implement interfaces extending from BaseView, and every presenter should implement interfaces extending from BasePresenter. In MVP, the communication between the View and the Presenter is both ways (View <—> Presenter). This means that our Activity/Fragment should have an instance of the presenter and the presenter should have an instance of the view. Also, the Presenter should not contain any android component (easy check, you shouldn’t have any android, imports in the presenter). So, let’s create the View and the Presenter for MainActivity. I will organize the packages by feature because it is better when we follow some design patterns.

Now, we have MainActivity that implements MainView, and MainPresenterImpl that implements MainPresenter. We said that MainActivity should have an instance of MainPresenter and MainPresenterImpl should have an instance of MainView.

public interface MainPresenter extends BasePresenter<MainView> {
}

public class MainPresenterImpl implements MainPresenter {
    private MainView view;

    @Override
    public void bindView(MainView view) {
        this.view = view;
    }

    @Override
    public void dropView() {
        this.view = null;
    }
}

public interface MainView extends BaseView<MainPresenter> {
}

public class MainActivity extends AppCompatActivity implements MainView {
    //...
    private MainPresenter presenter;
   // ...
}

You will notice methods bindView() and dropView() in the presenter. That is because the view is responsible for its lifecycle, and it should inform the presenter about its presence/absence. In the lifecycle methods of the Activity/Fragment, we should call these methods. This should be done in one of these three pairs: In onCreate/onResume/onStart call bindView() and in onDestroy/onPause/onStart call dropView(). Use either one of the pairs, but you should not mix them. For example: Don’t call bindView() in onCreate and dropView() in onPause. Let’s call these methods in the MainActivity:

public class MainActivity extends AppCompatActivity implements MainView {
    //...
    private MainPresenter presenter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        presenter = new MainPresenterImpl();
        presenter.bindView(this);

        //....
    }

    @Override
    protected void onDestroy() {
        presenter.dropView();
        super.onDestroy();
    }
}

Next, we should define methods in MainView and MainPresenter. In MainPresenter we want to get the quotes (it doesn’t matter for the view if the presenter gets them from the network or from the database) so we’ll create a method getAllQuotes(), and in the MainView we want to display the quotes, so we’ll create a method displayQuotes(List<Quote> quotes). After adding these methods in the interfaces, we will get compiler errors in the MainActivity and MainPresenterImpl. We need to implement these new methods. In the MainActivity we’ll just create a new adapter with the quotes and pass the adapter in the recyclerView. In the Presenter, it gets little trickier. We will move the network and database code from MainActivity to MainPresenterImpl, and whenever we create an adapter and set it to the recyclerView, we change that with calling displayQuotes() method of the MainView.

public class MainActivity extends AppCompatActivity implements MainView {
    //...
    @Override
    public void displayQuotes(List<Quote> quotes) {
        QuotesAdapter adapter = new QuotesAdapter(quotes);
        rvQuotes.setAdapter(adapter);
    }
}

// in presenter when we get the quotes
if (view != null) {
    view.displayQuotes(quotes);
}

Moreover, because QuoteDatabase requires context, and we can’t have a context in the Presenter, instead of creating QuoteDao inside the Presenter, we create it in MainActivity and pass it into the Presenter via the constructor. Finally, in the onCreate() method of the Activity, we call presenter.getAllQuotes(); You can check out the mvp_basic branch.

What have we done now? We refactored the MainActivity, to follow the MVP design pattern. Now, MainActvity has the responsibility only to display the quotes. It doesn’t need to be unit tested, because it doesn’t contain any logic. We moved the logic into the Presenter. But unfortunately, also the Presenter is hard to test now. We will try to make it better in the next article.

ReactiveX in Android with an example – RxJava

Reading Time: 5 minutes

What is Reactive Programming?

Reactive programming is programming with asynchronous data streams. It enables to create streams of anything – events, fails, variables, messages and etc. By using reactive programming in your application, you are able to create streams which you can then perform actions while the data emitted by those created streams.

Observer Pattern

The observer pattern is a software design pattern which defines a one-to-many relationship between objects. It means if the value/state of the observed object is changed/modified, the other objects which are observing are getting notified and updated.

ReactiveX

ReactiveX is a polyglot implementation of reactive programming which extends observer pattern and provides a bunch of data manipulation operators, threading abilities.

RxJava

RxJava is the JVM implementation of ReactiveX.

  • Observable – is a stream which emits the data
  • Observer – receives the emitted data from the observable
    • onSubscribe() – called when subscription is made
    • onNext() – called each time observable emits
    • onError() – called when an error occurs
    • onComplete() – called when the observable completes the emission of all items
  • Subscription – when the observer subscribes to observable to receive the emitted data. An observable can be subscribed by many observers
  • Scheduler – defines the thread where the observable emits and the observer receives it (for instance: background, UI thread)
    • subscribeOn(Schedulers.io())
    • observeOn(AndroidSchedulers.mainThread())
  • Operators – enable manipulation of the streamed data before the observer receives it
    • map()
    • flatMap()
    • concatMap() etc.

Example usage on Android

Tools, libraries, services used in the example:

  • Libraries:
    • ButterKnife – simplifying binding for android views
    • RxJava, RxAndroid – for reactive libraries
    • Retrofit2 – for network calls
  • Fake online rest API:
  • Java object generator from JSON file

What we want to achieve is to fetch users from 1. show in RecyclerView and load todo list to show the number of todos in the same RecyclerView without blocking the UI.

Here we define our endpoints. Retrofit2 supports return type of RxJava Observable for network calls.

    @GET("/users")
    Observable<List<User>> getUsers();

    @GET("/users/{id}/todos")
    Observable<List<Todo>> getTodosByUserID(@Path("id") int id);

    @GET("/todos")
    Observable<List<Todo>> getTodos();

Let’s fetch users:

  • .getUsers – returns observable of a list of users
  • .subscribeOn(Schedulers.io()) – make getUser() performs on background thread
  • .observeOn(AndroidSchedulers.mainThread()) – we switch to UI thread
  • flatMap – we set data to RecyclerView and return Observable user list which will be needed in fetching todo list
    private Observable<User> getUsersObservable() {
        return ServicesProvider.getDummyApi()
                .getUsers()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap((Function<List<User>, ObservableSource<User>>) users -> {
                    adapterRV.setData(users);
                    return Observable.fromIterable(users);
                });
    }

Now, fetch todo list of users using the 2nd endpoint.

Since we are not going to make another call, we don’t need Observable type in return of this method. So, here we use map() instead of flatMap() and we return User type.

    private Observable<User> getTodoListByUserId(User user) {
        return ServicesProvider.getDummyApi()
                .getTodosByUserID(user.getId())
                .subscribeOn(Schedulers.io())
                .map(todoList -> {
                    sleep();
                    user.setTodoList(todoList);
                    return user;
                });
    }

Now, fetch todo list of users using the 3rd endpoint.

The difference to the 2nd endpoint is that this returns a list of todos for all users. Here we can see the usage of filter() operator.

    private Observable<User> getAllTodo(User user) {
        return ServicesProvider.getDummyApi()
                .getTodos()
                .subscribeOn(Schedulers.io())
                .flatMapIterable((Function<List<Todo>, Iterable<Todo>>) todoList -> todoList)
                .filter(todo -> todo.getUserId().equals(user.getId()) && todo.getCompleted())
                .toList().toObservable()
                .map(todoList -> {
                    sleep();
                    user.setTodoList(todoList);
                    return user;
                });
    }
  • .flatMapIterable() – is used to convert Observable<List<T>> to Observable<T> which is needed for filter each item in list
  • .filter() – we filter todos to get each user’s completed todo list
  • .toList().toObservable() – for converting back to Observable<List<T>>
  • .map() – we set filtered list to user object which will be used in next code snippet

Now, the last step, we call the methods:

        getUsersObservable()
                .subscribeOn(Schedulers.io())
                .concatMap((Function<User, ObservableSource<User>>) this::getTodoListByUserId) // operator can be concatMap()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<User>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        disposables.add(d);
                    }

                    @Override
                    public void onNext(User user) {
                        adapterRV.updateData(user);
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "completed!");
                    }
                });
  • subscribeOn() – makes the next operator performed on background
  • concatMap() – here we call one of our methods getTodoListByUserId() or getAllTodo()
  • .observeOn(), .subscribe() – every time the user’s todo list is fetched from api in background thread, it emits the data and triggers onNext() so we update RecyclerView in UI thread
  • Left
    • getTodoListByUserId()
    • flatMap()
  • Right
    • concatMap()
    • getAllTodo() – filter usage

Difference between flatMap and concatMap is that the former is done in an arbitrary order but the latter preserves the order

Disposable

When an observer subscribes to an observable, a disposable object is provided in onSubscribe() method so it can later be used to terminate the background process to avoid it returning from callback to a dead activity.

private CompositeDisposable disposables = new CompositeDisposable();

observableobject.subscribe(new Observer() {
    @Override
    public void onSubscribe(Disposable d) {
        disposables.add(d);
    }

@Override
protected void onDestroy() {
    super.onDestroy();
    disposables.dispose();
}

Summary

In this post, I tried to give brief information about reactive programming, observer pattern, ReactiveX library and a simple example on android.

Why should you use RxJava in your projects?

  • less boilerplate code
  • easy thread management
  • thread-safety
  • easy error handling

Gitlab Repository

Example sourcecode: https://gitlab.com/47northlabs/public/android-rxjava

Links

Why I loved Flutter despite being a fan of Native App Development

Reading Time: 5 minutes

In this post, I am going to write about Flutter based on my opinions. Please don’t hesitate to comment if you want to discuss, support or contradict any of the mentioned points 🙂

What is Flutter

“Flutter is Google’s portable UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.” (from official Flutter website)

In the mobile development perspective, Flutter is a cross-platform mobile application development framework that is developed by Google and supports building apps for IOS and Android operating systems from a single codebase with native-like performance.

Cross-platform frameworks in general

I was always against developing apps with cross-platform tools however they are good options in terms of required resources such as they require less development time, less budget, less effort to maintain/support, etc. But for me, these factors are not much important somehow. Which things I didn’t like and made me avoid learning/developing cross-platform are:

  • Low performance, Poor UI – to be honest, I haven’t even played around with any cross-platform development. The fact which makes me come up with the ideas of cross-platform having low performance and poor UI is originated from the impressions I got from the usage cross-platform developed apps. Most of the time when I start using apps and see the bad performance and UI, unfortunately, I end up knowing that they are cross-platform developed apps.
  • It requires learning a new language – since I’m an android developer, I would rather choose to learn Swift for native IOS development so I will be able to develop for both platforms.

Dart language

Dart is a client-optimized programming language for fast apps on multiple platforms. It is developed by Google and is used to build mobile, desktop, backend and web applications. Dart is an object-oriented, class defined, garbage-collected language using a C-style syntax that transcompiles optionally into JavaScript. (wikipedia)

from dart.dev

Dart code is capable of being compiled into machine code. For mobile application development, Flutter uses this type of compilation which is Ahead-of-time compilation. This is the reason which makes Flutter distinguish from other cross-platform app development frameworks and lets an app developed in Flutter have native-like performance.

Things I liked about Flutter development

  • Faster code writing
  • Rich in UI components – lots of flutter widgets to build eye-catching UI
  • Native-like application performance
  • Instant hot reload – see the changes within seconds
  • No need to extra code to support older Android versions

Limitation

  • Less third-party libraries are available

Conclusion

The reason I wrote this blogpost is that Flutter caught my attention and I decided to give it a try. So I cloned the main page of the simple news application which is in the Play Store. Then I started to love Flutter because while the development process I easily found all the widgets that I needed to make the clone app look similar as much as possible to the original app. In the end, the result impressed me so much and I was convinced to learn more about flutter development.

I would also recommend you to give it a try. Especially, if you need to have MVP for your project then go for it. You will benefit from the advantages of cross-platform development such as faster development, less time to test, less effort to bugfix and maintenance and advantages of flutter such as native-like performance, great UI.

There is also one more thing I want to mention that, apps for Google’s Fuchsia OS are written in Flutter. I am not going to write about Fuchsia OS here, but briefly, it is assumed that Fuchsia OS can be Google’s next mobile OS or maybe replacement to Android OS (depending on the result of the problem between Google and Oracle). But it is hard to say how probable is that to happen. Anyway… The reason I mentioned this issue is to show you that – assuming that happened, then it is gonna be really valuable if you already know and have experience with Flutter. 🙂

Helpful resources which I think are worth to check:

My expectations from UIKonf Berlin 2019

Reading Time: 3 minutes

After more than 6 years this will be my first conference. This time as a guest. I remember well the moments of my presentation in Baku, Azerbaijan in 2013. Stressful experience, a lot of sweat, with my suit that was one number larger than it should be. That was 2013 and the topics were economics and management. Now I’m an experienced iOS developer and going to Berlin for a conference that should give me new ideas for personal development, but also to give me directions how to think for the future challenges.

Conference generalities

The venue of the UIKonf is RADIALSYSTEM V in Berlin.

From 26-29 May 2019 in Berlin, some serious iOS developers will come from big companies that will present their experiences in the iOS World.

The motto of the conference is simple:

“UIKonf is an independent conference for serious iOS developers”

Speakers

For this conference, the organizers decided to have only female speakers. All will come from big companies and with big experiences. We have female iOS developers who work or worked for Uber, Slack, LinkedIn…

Part of the speakers

Costs

The travel expenses are the cheapest for this conference viewing from my perspective. Fortunately, we Macedonians have direct flight connection to Berlin. The cost is around 150€.

The ticket to attend the conference costs 539€.

They are also offering free tickets to members of underrepresented groups in tech (this includes disabled people and generally people who are unable to attend without financial assistance).

The accommodation is something that is not completed from my side. The hotels near the conference building costs around 100€ per day.

Schedule

This conference has an excellent organization.

The first day 26 May is the day for social meetings, time for visiting places in Berlin, acknowledge the other attendants and will finish with a kick-off party where the people will take their badges.

Next two days 27 and 28 May are the days when the speakers will expose their presentations. 30 minutes per presentation with question and answers part of the end of each presentation.

29 May is the last day. This is unofficial part of the conference and the people can meet with the sponsors, dissect code problems in lab sessions with experienced experts, signup for a workshop or just hang out and code with new friends.

Expectations

I have big expectations. The names of the speakers, their experience, the companies they worked for are guarantees for something big and something good.

I’m going by myself and expect to meet new people, make new friendships, share experiences.

Berlin as a city is something new for me. I had heard very good things of Berlin. Good parties, restaurants, parks, two different sides of the city. I have friends that live/lived there.

With the correct size of my suit this time – it should be a good experience in my life.

Hackdayz #18: SMS Forwarding Android App

Reading Time: 5 minutes

Team members

Youssef Idelhoussain, Senior Front-end Engineer
Shehab Eltobgy, Test Manager

Abstract

This is the real deal.. Prepare your battery, connect to a good network to get thousands of SMS. Whoever you are, maybe you come from a faraway land, maybe you don’t understand my language, maybe you are from a country that I never heard the name of…
One thing is for sure, you will get the SMS. So, whoever you are, wherever you are, our app has special skills which will make this world easier for you, starting with getting an SMS 📲🤩

Having such nice days during our Hackdayz did not prevent us from thinking into adding more practical benefit to our company by improving the current app. And after we had our lunch, we had the power to start working, nevertheless, my vegetarian lunch did not taste good at all.
Our aim was by the end of Hackdayz that the app should be released in PlayStore and the code should be made as open-source for further improvements!

Side Notes

The app should have:

  • Rules: number and where it should be posted
  • Environment: Slack, Email, and others…
  • Some new settings: such as the ability for the user to set a password… (we were so optimistic)

Agenda

  • What is the problem you want to solve?
  • Who experiences that problem?
  • How do you want to solve that problem?
  • Why is this a better solution?

Having such a funny combination of a team with a front-end developer and test manager trying to develop an android app included so much fun these days, as we were literally underdogs. But, just to get our spirit up, we went to the gym, and then to the sauna where I could not even see my hands, and finally to the swimming pool.

Working on the project 👨‍💻 at Hackdayz18 in St. Gallen

Although, we were so ambitious that we set our plan to create an app with an infinite number of environments, and with so flexible rules (such as amateur dreams). After some time as Thomas A. Edison stated “I have not failed. I’ve just found 10,000 ways that won’t work.”, we realized that we are not gonna create the app as it actually was planned 🤯. Nevertheless, the days were cool enough to make us laugh while we were failing for several times.

Youssef was really ambitious that he told me “I will never go to bed mad. I’m gonna stay up and fight!”. After 10 minutes, each of us went to his room to sleep 😴. Due to the effort, I spent during 3 hours in the gym, sauna, and swimming, I wanted to sleep because by looking at my hand I couldn’t recognize how many fingers I did have.

The next day, we started working again. I wanted to start now with my real work since when kings start the party 🎉, my first task was to find a beautiful design. I decided to choose a simple design due to the time pressure. Besides improvisation is too good to leave to chance.

GitLab Repository

And using the mentioned GitLab we were able to create the app.

https://gitlab.com/47northlabs/public/sms-to-slack

Results

We were somehow not so much satisfied with the results, actually shocked 😱😱😱!!!

  • The app has been developed with the ability to add up to 5 environments. Unlike what we have expected to reach an infinite number of environments.. such youth dreams 😅
  • The app could not set the email as one of the environment due to inability to find a library via which the app can send the message to the email while it is in the background…. experience is simply the name we gave our mistakes 😄

Conclusion and implication

The app has been created successfully and applied to one of our android devices using +41 76 75x xxxx.

Screenshot of our Slack and Slackbot channel

Future features and challenges would be…

1. Adding email as a new environment. Let us see how this gonna be manageable 🤔.
2. Adding a password for the app. We are still so optimistic 😁.
3. Adding the ability to add more (unlimited environments) with the recycle bin 🧹 to remove them when needed.

By the end of the day, I just was totally shocked f the difference between what has been planned and what actually has been achieved. But, it was just a funny and exhausting experience.