ICore
iCore is a comprehensive Android library designed to streamline the development process by providing a robust set of modular components. With built-in support for the Model-View-ViewModel (MVVM) architecture, iCore offers a collection of base classes, extension functions, and utility classes to help developers create efficient and maintainable code
Install / Use
/learn @issever22/ICoreREADME
iCore
iCore is a library designed to simplify Android application development. It provides a comprehensive and standardized structure for applications using the MVVM (Model-View-ViewModel) architecture.
-
Reducing Code Duplication: By abstracting commonly used operations, it prevents the writing of repetitive code.
-
Quick Start: With ready-to-use core components, it allows for a quick start to projects.
-
Easy Integration: Easily integrates common operations like Retrofit, LiveData observation, and theme/language selection.
-
Reactive Data Management with Kotlin Flow and LiveData: iCore manages asynchronous data streams using Kotlin Flow and handles UI updates with LiveData, offering a more reactive and modern data processing model.
-
Extensive Extensions: Its extensible structure allows for customization according to application needs.
-
Centralized Management: Provides centralized management by allowing easy access to application resources with
ResourceProvider.
Sample Application
To better understand how to use the iCore library and to see its architecture in action, check out the sample application included in the app module of this repository.
You can find the sample application here.
Installation
Gradle
Add the following line to your project's build.gradle file:
allprojects {
repositories {
...
maven { url = uri("https://jitpack.io") }
}
}
Add the dependency to your module's build.gradle file:
dependencies {
implementation("com.github.issever22:iCore:v1.1.5")
}
Getting Started
Initialize the iCore library in your application's Application class:
class YourAppClass : Application() {
override fun onCreate() {
super.onCreate()
val coreOptions = CoreOptions().apply {
localDataClass = YourLocalData::class.java // Default is CoreLocalData
errorMessageField = "errorMessage" // Default is "message"
}
IsseverCore.init(this, coreOptions)
}
}
Note:
Using CoreOptions is optional. If you do not provide any options, default options will be used. If you provide a localDataClass, it must extend BaseLocalData.
Example LocalData class:
object YourLocalData : BaseLocalData() {
suspend fun someFunctions() : Resource<SomeModel> {
// This method utilizes a core function from iCore to perform a database operation
return databaseOperation({
getStringData("some_data")
})
}
}
Basic Usage
BaseActivity
Create your activities by extending BaseActivity:
class YourActivity : BaseActivity<ActivityYourBinding, YourViewModel>() {
override fun initViewBinding() = ActivityYourBinding.inflate(layoutInflater)
override val viewModel: YourViewModel by lazy {
ViewModelProvider(this)[YourViewModel::class.java]
}
override fun init() {
super.init()
// Additional initialization here
}
}
Note: If your activity or fragment does not require a ViewModel, you can use Nothing in place of YourViewModel. For example:
class YourActivity : BaseActivity<ActivityYourBinding, Nothing>() {
override fun initViewBinding() = ActivityYourBinding.inflate(layoutInflater)
override fun init() {
super.init()
// Additional initialization here
}
}
BaseFragment
Create your fragments by extending BaseFragment:
class YourFragment : BaseFragment<FragmentYourBinding, YourViewModel>() {
override fun initViewBinding() = FragmentYourBinding.inflate(layoutInflater)
override val viewModel: YourViewModel by lazy {
// currentActivity is a property provided by BaseFragment from iCore
ViewModelProvider(currentActivity)[YourViewModel::class.java]
}
override fun init() {
super.init()
// Set the LifecycleOwner for LiveData observation
binding.lifecycleOwner = this
// If needed, set the loading view to be shown during loading states
loadingView = binding.progressBar
// If needed, set the back button to navigate up when pressed
backButton = binding.yourBackButtonImageView
}
override fun initObservers() {
super.initObservers()
// This is an extension function provided by iCore to observe LiveData
observe(viewModel.navigateToSomewhere) {
if (it) {
// This is an extension function provided by iCore to navigation
navigateToActivity(
SomeOtherActivity::class.java, // Required
finishActivity = true, // Default is false
bundle = null // Default is null
)
}
}
}
}
BaseViewModel
Create your ViewModels by extending BaseViewModel:
class YourViewModel(
private val repository: YourRepository
) : BaseViewModel() {
private val _navigateToMain = MutableLiveData<Boolean>()
val navigateToMain: MutableLiveData<Boolean>
get() = _navigateToMain
fun login() {
collectData(
{
repository.login(SomeRequestModel()))
}, successAction = {
showSuccessSnackbar(R.string.login_success)
_navigateToMain.value = true
}
)
}
fun sampleFunction() {
collectData(
// Required: The function to be executed to fetch data from the repository
operation = {
repository.sampleFunction()
},
// Optional: Action to perform when the data fetching is successful
successAction = {
// Handle the successful response
},
// Optional: Action to perform when there is an error in fetching data
errorAction = { message, errorBody ->
// Handle the error
},
// Optional: Action to perform while the data is being loaded
loadingAction = {
// Show loading state
},
// Optional: Type of Snackbar to display for the error, success, etc.
stateType = StateType.DEFAULT,
// Optional: Text to display on the Snackbar action button
actionText = "Action",
// Optional: Action to perform when the Snackbar action button is clicked
snackBarAction = {
// Perform action when Snackbar action is triggered
},
// Optional: Lambda to determine whether the error message should be shown
shouldShowError = { message, errorBody ->
// Return true to show the error, false to suppress it
message != "Specific message to suppress"
}
)
}
}
BaseAdapter
Adapter Class
Create your adapter by extending BaseAdapter:
class YourAdapter : BaseAdapter<YourModel, ItemYourBinding>(ItemYourBinding::inflate) {
override fun bind(holder: BaseViewHolder, item: YourModel, context: Context) {
holder.apply {
binding.tvYourTextView.text = item.someProperty
}
}
}
Activity or Fragment
Use your adapter in an Activity or Fragment:
val adapter = YourAdapter()
binding.recyclerView.adapter = adapter
adapter.submitList(yourList)
adapter.setOnItemClickListener { item ->
// Do something with item
}
// `view` is the specific View within the item layout that was clicked.
// This allows you to perform actions or access properties of the clicked View directly.
adapter.setOnItemViewClickListener { item, view ->
when (view.id) {
R.id.someId -> //Some View clicked
else -> //Root View clicked
}
}
Retrofit Setup
To create a Retrofit instance using iCore, follow these steps:
Network Module
Initialize Retrofit in a singleton object or a class. You can add headers if needed.
object RetrofitInstance {
private val headers = mapOf("SomeKey" to "SomeValue")
val retrofit: Retrofit by lazy {
CoreNetwork.provideRetrofit("https://yourapi.baseurl.com/", headers)
}
}
Remote Data
Create a remote data source class by extending BaseRemoteData:
class SomeRemoteData(private val service: SomeService) : BaseRemoteData {
suspend fun login(user: SomeRequestModel): Resource<SomeResponseModel> {
return responseHandler({ service.login(user) })
}
// If needed, you can add additional actions or convert the response body to the desired entity.
suspend fun login(user: SomeRequestModel): Resource<SomeResponseConvertedEntity> {
return responseHandler({ service.login(user) }, entityConverter = { response ->
// Here you can convert the response to the desired entity.
}, doThenOnIO = { response ->
// Here you can perform additional operations in the IO thread.
})
}
}
Repository
Create a repository class by extending BaseRepository:
class SomeRepository(private val remoteData: SomeRemoteData) : BaseRepository {
suspend fun login(user: SomeRequestModel): Flow<Resource<SomeResponseModel>> {
return emitResult({ remoteData.login(user) })
}
// If needed, you can add additional actions.
suspend fun login(user: SomeRequestModel): Flow<Resource<SomeResponseModel>> {
return emitResult({ remoteData.login(user) }, doThenOnMain = { resource ->
// Here you can perform additional operations on the Main Thread.
})
}
}
Extension Functions
Example Usages
iCore provides various useful extension functions. Here are a few examples:
Endless Scroll
// Adds an endless scroll listener to the Rec
