Enhancing Network Requests by Chaining or Zipping with RxJava

In Eventyay Attendee, making HTTP requests to fetch data from the API is one of the most basic techniques used. RxJava comes in as a great method to help us making asynchronous requests and optimize the code a lot. This blog post will deliver some advanced RxJava used in Eventyay Attendee. Why using RxJava?Advanced RxJava Technique – Chaining network calls with RxJavaAdvanced RxJava Technique – Merging network calls with RxJavaConclusionsResources WHY USING RXJAVA? There are many reasons why RxJava is a great API in Android Development. RxJava is an elegant solution to control data flow in programming, where developers can cache data, get data, update the UI after getting the data, handle asynchronous tasks. RxJava also works really well with MVVM architectural pattern. CHAINING NETWORK CALLS WITH RXJAVA Chaining RxJava is a technique using flatMap() operator of Rxjava. It will use the result from one network call in order to make the next network call.  In Eventyay Attendee, this technique is used when we want to update the user profile image. First, we need to upload the new profile image to the server in order to get the image URL, and then we use that URL to update the user profile compositeDisposable += authService.uploadImage(UploadImage(encodedImage)).flatMap { authService.updateUser(user.copy(avatarUrl = it.url)) }.withDefaultSchedulers() .doOnSubscribe { mutableProgress.value = true } .doFinally { mutableProgress.value = false } .subscribe({ mutableMessage.value = resource.getString(R.string.user_update_success_message) Timber.d("User updated") }) { mutableMessage.value = resource.getString(R.string.user_update_error_message) Timber.e(it, "Error updating user!") } In conclusion, zipping RxJava helps to make HTTP requests more continuous and reduce unnecessary codes.  ZIPPING NETWORK CALLS WITH RXJAVA Zipping RxJava is a technique using zip() operator of Rxjava. It will wait for items from two or more Observables to arrive and then merge them together for emitting. This technique would be useful when two observables emit the same type of data. In Eventyay Attendee, this technique is used when fetching similar events by merging events in the same location and merging events in the same event type. var similarEventsFlowable = eventService.getEventsByLocationPaged(location, requestedPage, 3) if (topicId != -1L) { similarEventsFlowable = similarEventsFlowable .zipWith(eventService.getSimilarEventsPaged(topicId, requestedPage, 3), BiFunction { firstList: List<Event>, secondList: List<Event> -> val similarList = mutableSetOf<Event>() similarList.addAll(firstList + secondList) similarList.toList() }) } compositeDisposable += similarEventsFlowable .take(1) .withDefaultSchedulers() .subscribe({ response -> ... }, { error -> ... }) In conclusion, zipping RxJava helps running all the tasks in parallel and return all of the results in a single callback. CONCLUSION Even though RxJava is pretty hard to understand and master, it is a really powerful tool in Android Development and MVVM models. These techniques above are really simple to implement and they could improve the app by r RESOURCES Eventyay Attendee Source Code:  https://github.com/fossasia/open-event-attendee-android/pull/2010 https://github.com/fossasia/open-event-attendee-android/pull/2117 RxJava Documentation: http://reactivex.io/documentation

Continue ReadingEnhancing Network Requests by Chaining or Zipping with RxJava

Implementing Stripe payment in Eventyay Attendee

In Eventyay Attendee, getting tickets for events has always been a core function that we focus on. When searching for events based on location, autosuggestion based on user input really comes out as a great feature to increase the user experience. Let’s take a look at the implementation Why using Stripe?Implementing Stripe Payment in Eventyay AttendeeConclusionResources WHY USING STRIPE? There are many great APIs to be taken into consideration for making payments but we choose Stripe as one of our payment gateways because of simple implementations, detailed documentation, a good number of supported card type and good security support IMPLEMENTING STRIPE PAYMENT IN EVENTYAY ATTENDEE Step 1: Setup dependency in the build.gradle // Stripe implementation 'com.stripe:stripe-android:10.3.0' Step 2: Set up UI to take card information The information needed for making payments are Card Number, CVC, Expiration Date, which can be made with simple UI (EditText, Spinner,…). Stripe support getting information with CardInputWidget but we made a custom UI for that. Here is the UI we created. Step 3: Create a card and validate information Stripe has an object called Card, which takes card number, expiration date and CVC number as parameter to detect the card type and validate the card information with function .validateCard() PAYMENT_MODE_STRIPE -> { card = Card.create(rootView.cardNumber.text.toString(), attendeeViewModel.monthSelectedPosition, rootView.year.selectedItem.toString().toInt(), rootView.cvc.text.toString()) if (!card.validateCard()) { rootView.snackbar(getString(R.string.invalid_card_data_message)) false } else { true } } Step 4: Send the token to the server If card information is valid, we can create a token from the Card and then send it to the server. The token will act as the identifier of the card in order for the server to charge the payment and create tickets for the user.  private fun sendToken(card: Card) { Stripe(requireContext()) .createToken(card, BuildConfig.STRIPE_API_KEY, object : TokenCallback { override fun onSuccess(token: Token) { val charge = Charge(attendeeViewModel.getId().toInt(), token.id, null) attendeeViewModel.chargeOrder(charge) } override fun onError(error: Exception) { rootView.snackbar(error.localizedMessage.toString()) } }) } Step 5: So the rest is already handled by the server. Android application will then just receive the response from the server to see if the order is charged successfully or not. CONCLUSION With Stripe, user can easily make payments to get tickets for events. Stripe is a great payment gateway as it is really easy to implement in Android. Hopefully, this blog post will help you create a great shopping cart app or any kind of application that requires fast, simple and easy payments. RESOURCES Eventyay Attendee Pull Request on Stripe: https://github.com/fossasia/open-event-attendee-android/pull/1863 Documentation from Stripe for Android: https://stripe.com/docs/mobile/android

Continue ReadingImplementing Stripe payment in Eventyay Attendee

Implementing pagination with Retrofit in Eventyay Attendee

Pagination (Paging) is a common and powerful technique in Android Development when making HTTP requests or fetching data from the database. Eventyay Attendee has found many situations where data binding comes in as a great solution for our network calls with Retrofit. Let’s take a look at this technique. Problems without Pagination in Android DevelopmentImplementing Pagination with Kotlin with RetrofitResults and GIFConclusions PROBLEMS WITHOUT DATABINDING IN ANDROID DEVELOPMENT Making HTTP requests to fetch data from the API is a basic work in any kind of application. With the mobile application, network data usage management is an important factor that affects the loading performance of the app. Without paging, all of the data are fetched even though most of them are not displayed on the screen. Pagination is a technique to load all the data in pages of limited items, which is much more efficient IMPLEMENTING DATABINDING IN FRAGMENT VIEW Step 1:  Set up dependency in build.gradle // Paging implementation "androidx.paging:paging-runtime:$paging_version" implementation "androidx.paging:paging-rxjava2:$paging_version" Step 2:  Set up retrofit to fetch events from the API @GET("events?include=event-sub-topic,event-topic,event-type") fun searchEventsPaged( @Query("sort") sort: String, @Query("filter") eventName: String, @Query("page[number]") page: Int, @Query("page[size]") pageSize: Int = 5 ): Single<List<Event>> Step 3: Set up the DataSource DataSource is a base class for loading data in the paging library from Android. In Eventyay, we use PageKeyedDataSource. It will fetch the data based on the number of pages and items per page with our default parameters. With PageKeyedDataSource, three main functions loadInitial(), loadBefore(), loadAfter() are used to to load each chunks of data. class EventsDataSource( private val eventService: EventService, private val compositeDisposable: CompositeDisposable, private val query: String?, private val mutableProgress: MutableLiveData<Boolean> ) : PageKeyedDataSource<Int, Event>() { override fun loadInitial( params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Event> ) { createObservable(1, 2, callback, null) } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Event>) { val page = params.key createObservable(page, page + 1, null, callback) } override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Event>) { val page = params.key createObservable(page, page - 1, null, callback) } private fun createObservable( requestedPage: Int, adjacentPage: Int, initialCallback: LoadInitialCallback<Int, Event>?, callback: LoadCallback<Int, Event>? ) { compositeDisposable += eventService.getEventsByLocationPaged(query, requestedPage) .withDefaultSchedulers() .subscribe({ response -> if (response.isEmpty()) mutableProgress.value = false initialCallback?.onResult(response, null, adjacentPage) callback?.onResult(response, adjacentPage) }, { error -> Timber.e(error, "Fail on fetching page of events") } ) } } Step 4: Set up the Data Source Factory DataSourceFactory is the class responsible for creating DataSource object so that we can create PagedList (A type of List used for paging) for events. class EventsDataSourceFactory( private val compositeDisposable: CompositeDisposable, private val eventService: EventService, private val query: String?, private val mutableProgress: MutableLiveData<Boolean> ) : DataSource.Factory<Int, Event>() { override fun create(): DataSource<Int, Event> { return EventsDataSource(eventService, compositeDisposable, query, mutableProgress) } } Step 5: Adapt the current change to the ViewModel.  Previously, events fetched in List<Event> Object are now should be turned into PagedList<Event>. sourceFactory = EventsDataSourceFactory( compositeDisposable, eventService, mutableSavedLocation.value, mutableProgress ) val eventPagedList = RxPagedListBuilder(sourceFactory, config) .setFetchScheduler(Schedulers.io()) .buildObservable() .cache() compositeDisposable += eventPagedList .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .distinctUntilChanged() .doOnSubscribe { mutableProgress.value = true }.subscribe({ val currentPagedEvents = mutablePagedEvents.value if…

Continue ReadingImplementing pagination with Retrofit in Eventyay Attendee

Adding time counter on ordering tickets in Eventyay Attendee

In Eventyay Attendee, ordering tickets for events has always been a core functionality that we focus on. When ordering tickets, adding a time counter to make a reservation and release tickets after timeout is a common way to help organizers control their tickets’ distribution and help users save up their tickets. Let’s take a look at how to implement this feature Implementing the time counter Some notes on implementing time counterConclusionResources INTEGRATING TIME COUNTER TO YOUR SYSTEM Step 1: Create the UI for your time counter. In here, we made a simple View container with TextView inside to update the time. Step 2: Set up the time counter with Android CountdownTimer with the total countdown time and the ticking time. In Eventyay, the default countdown time is 10 minutes (600,000 ms) with the ticking time is (1,000 ms), which means the UI is updated every one second. private fun setupCountDownTimer(orderExpiryTime: Int) { rootView.timeoutCounterLayout.isVisible = true rootView.timeoutInfoTextView.text = getString(R.string.ticket_timeout_info_message, orderExpiryTime.toString()) val timeLeft: Long = if (attendeeViewModel.timeout == -1L) orderExpiryTime * 60 * 1000L else attendeeViewModel.timeout timer = object : CountDownTimer(timeLeft, 1000) { override fun onFinish() { findNavController(rootView).navigate(AttendeeFragmentDirections .actionAttendeeToTicketPop(safeArgs.eventId, safeArgs.currency, true)) } override fun onTick(millisUntilFinished: Long) { attendeeViewModel.timeout = millisUntilFinished val minutes = millisUntilFinished / 1000 / 60 val seconds = millisUntilFinished / 1000 % 60 rootView.timeoutTextView.text = "$minutes:$seconds" } } timer.start() } Step 3: Set up creating a pending order when the timer starts counting so that users can hold a reservation for their tickets. A simple POST request about empty order to the API is made fun initializeOrder(eventId: Long) { val emptyOrder = Order(id = getId(), status = ORDER_STATUS_INITIALIZING, event = EventId(eventId)) compositeDisposable += orderService.placeOrder(emptyOrder) .withDefaultSchedulers() .subscribe({ mutablePendingOrder.value = it orderIdentifier = it.identifier.toString() }, { Timber.e(it, "Fail on creating pending order") }) } Step 4: Set up canceling order when the time counter finishes. As time goes down, the user should be redirected to the previous fragment and a pop-up dialog should show with a message about reservation time has finished. There is no need to send an HTTP request to cancel the pending order as it is automatically handled by the server. Step 5: Cancel the time counter in case the user leaves the app unexpectedly or move to another fragment. If this step is not made, the CountdownTimer still keeps counting in the background and possibly call onFinished() at some point that could evoke functions and crash the app override fun onDestroy() { super.onDestroy() if (this::timer.isInitialized) timer.cancel() } RESULTS CONCLUSION For a project with a ticketing system, adding a time counter for ordering is a really helpful feature to have. With the help of Android CountdownTimer, it is really to implement this function to enhance your user experience. RESOURCES Eventyay Attendee Android Codebase: https://github.com/fossasia/open-event-android Eventyay Attendee Android PR: #1843 - Add time counter on ordering ticket Documentation: https://developer.android.com/reference/android/os/CountDownTimer

Continue ReadingAdding time counter on ordering tickets in Eventyay Attendee

Implementing places autosuggestion with Mapbox for searching events in Eventyay Attendee

In Eventyay Attendee, searching for events has always been a core function that we focus on. When searching for events based on location, autosuggestion based on user input really comes out as a great feature to increase the user experience. Let’s take a look at the implementation Why using Mapbox?Integrating places autosuggestion for searchingConclusionResources WHY USING MAPBOX? There are many Map APIs to be taken into consideration but we choose Mapbox as it is really to set up and use, good documentation and reasonable pricing for an open-source project compared to other Map API. INTEGRATING PLACES AUTOSUGGESTION FOR SEARCHING Step 1: Setup dependency in the build.gradle + the MAPBOX key //Mapbox java sdk implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:4.8.0' Step 2: Set up functions inside ViewModel to handle autosuggestion based on user input: private fun loadPlaceSuggestions(query: String) { // Cancel Previous Call geoCodingRequest?.cancelCall() doAsync { geoCodingRequest = makeGeocodingRequest(query) val list = geoCodingRequest?.executeCall()?.body()?.features() uiThread { placeSuggestions.value = list } } } private fun makeGeocodingRequest(query: String) = MapboxGeocoding.builder() .accessToken(BuildConfig.MAPBOX_KEY) .query(query) .languages("en") .build() Based on the input, the functions will update the UI with new inputs of auto-suggested location texts. The MAPBOX_KEY can be given from the Mapbox API. Step 3: Create an XML file to display autosuggestion strings item and set up RecyclerView in the main UI fragment Step 4: Set up ListAdapter and ViewHolder to bind the list of auto-suggested location strings. Here, we use CamenFeature to set up with ListAdapter as the main object. With the function .placeName(), information about the location will be given so that ViewHolder can bind the data class PlaceSuggestionsAdapter : ListAdapter<CarmenFeature, PlaceSuggestionViewHolder>(PlaceDiffCallback()) { var onSuggestionClick: ((String) -> Unit)? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaceSuggestionViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_place_suggestion, parent, false) return PlaceSuggestionViewHolder(itemView) } override fun onBindViewHolder(holder: PlaceSuggestionViewHolder, position: Int) { holder.apply { bind(getItem(position)) onSuggestionClick = this@PlaceSuggestionsAdapter.onSuggestionClick } } class PlaceDiffCallback : DiffUtil.ItemCallback<CarmenFeature>() { override fun areItemsTheSame(oldItem: CarmenFeature, newItem: CarmenFeature): Boolean { return oldItem.placeName() == newItem.placeName() } override fun areContentsTheSame(oldItem: CarmenFeature, newItem: CarmenFeature): Boolean { return oldItem.equals(newItem) } } } fun bind(carmenFeature: CarmenFeature) { carmenFeature.placeName()?.let { val placeDetails = extractPlaceDetails(it) itemView.placeName.text = placeDetails.first itemView.subPlaceName.text = placeDetails.second itemView.subPlaceName.isVisible = placeDetails.second.isNotEmpty() itemView.setOnClickListener { onSuggestionClick?.invoke(placeDetails.first) } } } Step 5: Set up RecyclerView with Adapter created above: private fun setupRecyclerPlaceSuggestions() { rootView.rvAutoPlaces.layoutManager = LinearLayoutManager(context) rootView.rvAutoPlaces.adapter = placeSuggestionsAdapter placeSuggestionsAdapter.onSuggestionClick = { savePlaceAndRedirectToMain(it) } } RESULTS CONCLUSION Place Autocorrection is a really helpful and interesting feature to include in your next project. With the help of Mapbox SDK, it is really easy to implement to enhance your user experience in your application. RESOURCES Eventyay Attendee Android Codebase: https://github.com/fossasia/open-event-android Eventyay Attendee PR: #1594 - feat: Mapbox Autosuggest Documentation: https://docs.mapbox.com/android/plugins/overview/places/

Continue ReadingImplementing places autosuggestion with Mapbox for searching events in Eventyay Attendee

Data Binding with Kotlin in Eventyay Attendee

Databinding is a common and powerful technique in Android Development. Eventyay Attendee has found many situations where data binding comes in as a great solution for our complex UI. Let’s take a look at this technique. Problems without data binding in Android DevelopmentImplementing Databinding with Kotlin inside FragmentImplementing Databinding with Kotlin inside RecyclerView/AdapterResults and GIFConclusions PROBLEMS WITHOUT DATABINDING IN ANDROID DEVELOPMENT Getting the data and fetching it to the UI is a basic work in any kind of application. With Android Development, the most common way to do is it to call function like .setText(), isVisible = True/False,.. in your fragment. This can create many long boilerplate codes inside Android classes. Databinding removes them and moves to the UI classes (XML). IMPLEMENTING DATABINDING IN FRAGMENT VIEW Step 1: Enabling data binding in the project build.gradle android { dataBinding { enabled = true } Step 2: Wrap the current layout with <layout></layout> tag. Inside that, put <data></data> to indicate any variables needed for data binding. For example, this code here display an event variable for our fragment about event details: <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:bind="http://schemas.android.com/tools"> <data> <variable name="event" type="org.fossasia.openevent.general.event.Event" /> </data> <androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/eventCoordinatorLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> Step 3: Bind your data in the XML file and create a Binding Adapter class for better usage With the setup above, you can start binding your data with “@{<data code here>}” <TextView android:id="@+id/eventName" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/layout_margin_large" android:layout_marginTop="@dimen/layout_margin_large" android:layout_marginRight="@dimen/layout_margin_large" android:text="@{event.name}" android:fontFamily="sans-serif-light" android:textColor="@color/dark_grey" android:textSize="@dimen/text_size_extra_large" app:layout_constraintEnd_toEndOf="@+id/eventImage" app:layout_constraintStart_toStartOf="@+id/eventImage" app:layout_constraintTop_toBottomOf="@+id/eventImage" tools:text="Open Source Meetup" /> Sometimes, to bind our data normally we need to use a complex function, then creating Binding Adapter class really helps. For example, Eventyay Attendee heavily uses Picasso function to fetch image to ImageView: @BindingAdapter("eventImage") fun setEventImage(imageView: ImageView, url: String?) { Picasso.get() .load(url) .placeholder(R.drawable.header) .into(imageView) } <ImageView android:id="@+id/eventImage" android:layout_width="@dimen/layout_margin_none" android:layout_height="@dimen/layout_margin_none" android:scaleType="centerCrop" android:transitionName="eventDetailImage" app:layout_constraintDimensionRatio="2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:eventImage="@{event.originalImageUrl}" app:layout_constraintTop_toBottomOf="@id/alreadyRegisteredLayout" /> Step 4: Finalize data binding setup in Android classes. We can create a binding variable. The binding root will serve as the root node of the layout. Whenever data is needed to be bind, set the data variable stated to that binding variable and call function executePendingBingdings() private lateinit var rootView: View private lateinit var binding: FragmentEventBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_event, container, false) rootView = binding.root binding.event = event binding.executePendingBindings() SOME NOTES In the example mentioned above, the name of the binding variable class is auto-generated based on the name of XML file + “Binding”. For example, the XML name was fragment_event so the DataBinding classes generated name is FragmentEventBinding.The data binding class is only generated only after compiling the project.Sometimes, compiling the project fails because of some problems due to data binding without any clear log messages, then that’s probably because of error when binding your data in XML class. For example, we encounter a problem when changing the value in Attendee data class from firstname to firstName but XML doesn’t follow the update. So make sure you bind your data correctly <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/layout_margin_large" android:textColor="@color/black" android:textSize="@dimen/text_size_expanded_title_large" android:text="@{attendee.firstname + ` ` + attendee.lastname}" tools:text="@string/name_preview" />…

Continue ReadingData Binding with Kotlin in Eventyay Attendee

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