Dependency Injection with Kotlin Koin in Eventyay Attendee

Eventyay Attendee Android app contains a lot of shared components between classes that should be reused. Dependency Injection with Koin really comes in as a great problem solver. Dependency Injection is a common design pattern used in various projects, especially with Android Development. In short, dependency injection helps to create/provide instances to the dependent class, and share it among other classes. Why using Koin?Process of setting up Koin in the applicationResultsConclusionResources Let’s get into the details WHY USING KOIN? Before Koin, dependency injection in Android Development was mainly used with other support libraries like Dagger or Guice. Koin is a lightweight alternative that was developed for Kotlin developers. Here are some of the major things that Koin can do for your project: Modularizing your project by declaring modulesInjecting class instances into Android classesInjecting class instance by the constructorSupporting with Android Architecture Component and KotlinTesting easily SETTING UP KOIN IN THE ANDROID APPLICATION Adding the dependencies to build.gradle // Koin implementation "org.koin:koin-android:$koin_version" implementation "org.koin:koin-androidx-scope:$koin_version" implementation "org.koin:koin-androidx-viewmodel:$koin_version" Create a folder to manage all the dependent classes. Inside this Modules class, we define modules and create “dependency” class instances/singletons that can be reused or injected. For Eventyay Attendee, we define 5 modules: commonModule, apiModule, viewModelModule, networkModule, databaseModule. This saves a lot of time as we can make changes like adding/removing/editing the dependency in one place. Let’s take a look at what is inside some of the modules: DatabaseModule val databaseModule = module { single { Room.databaseBuilder(androidApplication(), OpenEventDatabase::class.java, "open_event_database") .fallbackToDestructiveMigration() .build() } factory { val database: OpenEventDatabase = get() database.eventDao() } factory { val database: OpenEventDatabase = get() database.sessionDao() } CommonModule val commonModule = module { single { Preference() } single { Network() } single { Resource() } factory { MutableConnectionLiveData() } factory<LocationService> { LocationServiceImpl(androidContext()) } } ApiModule val apiModule = module { single { val retrofit: Retrofit = get() retrofit.create(EventApi::class.java) } single { val retrofit: Retrofit = get() retrofit.create(AuthApi::class.java) } NetworkModule single { val connectTimeout = 15 // 15s val readTimeout = 15 // 15s val builder = OkHttpClient().newBuilder() .connectTimeout(connectTimeout.toLong(), TimeUnit.SECONDS) .readTimeout(readTimeout.toLong(), TimeUnit.SECONDS) .addInterceptor(HostSelectionInterceptor(get())) .addInterceptor(RequestAuthenticator(get())) .addNetworkInterceptor(StethoInterceptor()) if (BuildConfig.DEBUG) { val httpLoggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } builder.addInterceptor(httpLoggingInterceptor) } builder.build() } single { val baseUrl = BuildConfig.DEFAULT_BASE_URL val objectMapper: ObjectMapper = get() val onlineApiResourceConverter = ResourceConverter( objectMapper, Event::class.java, User::class.java, SignUp::class.java, Ticket::class.java, SocialLink::class.java, EventId::class.java, EventTopic::class.java, Attendee::class.java, TicketId::class.java, Order::class.java, AttendeeId::class.java, Charge::class.java, Paypal::class.java, ConfirmOrder::class.java, CustomForm::class.java, EventLocation::class.java, EventType::class.java, EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, FavoriteEvent::class.java, Session::class.java, SessionType::class.java, MicroLocation::class.java, SpeakersCall::class.java, Sponsor::class.java, EventFAQ::class.java, Notification::class.java, Track::class.java, DiscountCode::class.java, Settings::class.java, Proposal::class.java) Retrofit.Builder() .client(get()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JSONAPIConverterFactory(onlineApiResourceConverter)) .addConverterFactory(JacksonConverterFactory.create(objectMapper)) .baseUrl(baseUrl) .build() } As described in the code, Koin support single for creating a singleton object, factory for creating a new instance every time an object is injected. With all the modules created, it is really simple to get Koin running in the project with the function startKoin() and a few lines of code. We use it inside the application class: startKoin { androidLogger() androidContext(this@OpenEventGeneral) modules(listOf( commonModule, apiModule, viewModelModule, networkModule, databaseModule )) } Injecting created instances defined in the modules can be used in two way, directly inside…

Continue ReadingDependency Injection with Kotlin Koin in Eventyay Attendee

Implementation of Sponsors API in Open Event Organizer Android App

New contributors to this project are sometimes not experienced with the set of libraries and MVP pattern which this app uses. This blog post is an attempt to walk a new contributor through some parts of the code of the app by implementing an operation for an endpoint of the API. We’ll be implementing the sponsor endpoint. Open Event Organizer Android app uses a robust architecture. It is presently using the MVP (Model-View-Presenter) architecture. Therefore, this blog post aims at giving some brief insights to the app architecture through the implementation Sponsor endpoint. This blog post will focus only on one operation of the endpoint - the list operation - so as to make the post short enough. This blog post relates to Pull Request #901 of Open Event Organizer App. Project structure: These are the parts of the project structure where major updates will be made for the implementation of Sponsor endpoint: core data Setting up elements in the data module for the respective endpoint Sponsor.java @Data @Builder @Type("sponsor") @AllArgsConstructor @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) @EqualsAndHashCode(callSuper = false, exclude = { "sponsorDelegate", "checking" }) @Table(database = OrgaDatabase.class) public class Sponsor extends AbstractItem<Sponsor, SponsorsViewHolder> implements Comparable<Sponsor>, HeaderProvider {    @Delegate(types = SponsorDelegate.class)    private final SponsorDelegateImpl sponsorDelegate = new         SponsorDelegateImpl(this); This class uses Lombok, Jackson, RaizLabs-DbFlow, extends AbstractItem class (from Fast Adapter) and implements Comparable and HeaderProvider. All the annotations processor help us reduce boilerplate code. From the Lombok plugin, we are using: Lombok has annotations to generate Getters, Setters, Constructors, toString(), Equal() and hashCode() methods. Thus, it is very efficient in reducing boilerplate code @Getter,  @Setter, @ToString, @EqualsAndHashCode @Data is a shortcut annotation that bundles the features of @Getter, @Setter, @ToString and @EqualsAndHashCode The @Delegate is used for direct calls to the methods that are annotated with it, to the specified delegate. It basically separates the model class from other methods which do not pertain to data. Jackson @JsonNaming - used to choose the naming strategies for properties in serialization, overriding the default. For eg:  KEBAB_CASE, LOWER_CASE, SNAKE_CASE, UPPER_CAMEL_CASE @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) @JsonProperty - used to store the variable from JSON schema as the given variable name. So, "type" from JSON will be stored as sponsorType. @JsonProperty("type") public String sponsorType; RaizLabs-DbFlow DbFlow uses model classes which must be annotated using the annotations provided by the library. The basic annotations are – @Table, @PrimaryKey, @Column, @ForeignKey etc. These will create a table named attendee with the columns and the relationships annotated. SponsorDelegate.java and SponsorDelegateImpl.java The above are required only for method declarations of the classes and interfaces that Sponsor.java extends or implements. These basically separate the required method overrides from the base item class. public class SponsorDelegateImpl extends AbstractItem<Sponsor, SponsorsViewHolder> implements SponsorDelegate { SponsorRepository.java and SponsorRepositoryImpl.java A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the…

Continue ReadingImplementation of Sponsors API in Open Event Organizer Android App

MVP in Loklak Wok Android using Dagger2

MVP stands for Model-View-Presenter, one of the most popular and commonly used design pattern in android apps. Where “Model” refers to data source, it can be a SharedPreference, Database or data from a Network call. Going by the word, “View” is the user interface and finally “Presenter”, it’s a mediator between model and view. Whatever events occur in a view are passed to presenter and the presenter fetches the data from the model and finally passes it back to the view, where the data is populated in ViewGroups. Now, the main question, why it is so widely used? One of the obvious reason is the simplicity to implement it and it completely separates the business logic, so, easy to write unit-tests. Though it is easy to implement, its implementation requires a lot of boilerplate code, which is one of its downpoints. But, using Dagger2 the boilerplate code can be reduced to a great extent. Let’s see how Dagger2 is used in Loklak Wok Android to implement MVP architecture. Adding Dagger2 to the project In app/build.gradle file dependencies { ... compile 'com.google.dagger:dagger:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }   Implementation First a contract is created which defines the behaviour or say the functionality of View and Presenter. Like showing a progress bar when data is being fetched, or the view when the network request is successful or it failed. The contract should be easy to read and going by the names of the method one should be able to know the functionality of methods. For tweet search suggestions, the contract is defined in SuggestContract interface. public interface SuggestContract { interface View { void showProgressBar(boolean show); void onSuggestionFetchSuccessful(List<Query> queries); void onSuggestionFetchError(Throwable throwable); } interface Presenter { void attachView(View view); void createCompositeDisposable(); void loadSuggestionsFromAPI(String query, boolean showProgressBar); void loadSuggestionsFromDatabase(); void saveSuggestions(List<Query> queries); void suggestionQueryChanged(Observable<CharSequence> observable); void detachView(); } }   A SuggestPresenter class is created which implements the SuggestContract.Presenter interface. I will not be explaining how each methods in SuggestPresenter class is implemented as this blog solely deals with implementing MVP. If you are interested you can go through the source code of SuggestPresenter. Similarly, the view i.e. SuggestFragment implements SuggestContract.View interface. So, till this point we have our presenter and view ready. The presenter needs to access the model and the view requires to have an instance of presenter. One way could be instantiating an instance of model inside presenter and an instance of presenter inside view. But, this way model, view and presenter would be coupled and that defeats our purpose. So, we just INJECT model into presenter and presenter into view using Dagger2. Injecting here means Dagger2 instantiates model and presenter and provides wherever they are requested. ApplicationModule provides the required dependencies for accessing the “Model” i.e. a Loklak API client and realm database instance. When we want Dagger2 to provide a dependency we create a method annotated with @Provides as providesLoklakAPI and providesRealm. @Provides LoklakAPI providesLoklakAPI(Retrofit retrofit) { return retrofit.create(LoklakAPI.class); } @Provides Realm providesRealm() { return Realm.getDefaultInstance(); }   If we look…

Continue ReadingMVP in Loklak Wok Android using Dagger2