Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Setting/#22] datastore 모듈 세팅 #23

Merged
merged 13 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ android {
dependencies {
implementation(project(":core:network"))
implementation(project(":core:model"))
}
implementation(project(":core:datastore"))
}
16 changes: 16 additions & 0 deletions core/data/src/main/java/com/teamwable/data/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.teamwable.data.di

import com.teamwable.data.repository.DummyRepository
import com.teamwable.data.repository.UserInfoRepository
import com.teamwable.data.repositoryimpl.DefaultDummyRepository
import com.teamwable.data.repositoryimpl.DefaultUserInfoRepository
import com.teamwable.datastore.datasource.DefaultWablePreferenceDatasource
import com.teamwable.datastore.datasource.WablePreferencesDataSource
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -14,4 +18,16 @@ internal abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindsHomeRepository(repositoryImpl: DefaultDummyRepository): DummyRepository

@Binds
@Singleton
abstract fun bindsWableLocalDataSource(
dataSourceImpl: DefaultWablePreferenceDatasource,
): WablePreferencesDataSource

@Binds
@Singleton
abstract fun bindsUserInfoRepository(
repositoryImpl: DefaultUserInfoRepository,
): UserInfoRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.teamwable.data.repository

import kotlinx.coroutines.flow.Flow

interface UserInfoRepository {
fun getAccessToken(): Flow<String>

fun getRefreshToken(): Flow<String>

fun getAutoLogin(): Flow<Boolean>

fun getNickname(): Flow<String>

fun getMemberId(): Flow<Int>

fun getMemberProfileUrl(): Flow<String>

fun getIsPushAlarmAllowed(): Flow<Boolean>

suspend fun saveAccessToken(accessToken: String)

suspend fun saveRefreshToken(refreshToken: String)

suspend fun saveAutoLogin(autoLogin: Boolean)

suspend fun saveNickname(name: String)

suspend fun saveMemberId(memberId: Int)

suspend fun saveMemberProfileUrl(memberUrl: String)

suspend fun saveIsPushAlarmAllowed(isPushAlarmAllowed: Boolean)

suspend fun clearAll()

suspend fun clearForRefreshToken()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.teamwable.data.repositoryimpl

import com.teamwable.data.repository.UserInfoRepository
import com.teamwable.datastore.datasource.WablePreferencesDataSource
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

internal class DefaultUserInfoRepository @Inject constructor(
private val wablePreferencesDataSource: WablePreferencesDataSource,
) : UserInfoRepository {
override fun getAccessToken(): Flow<String> =
wablePreferencesDataSource.accessToken

override fun getRefreshToken(): Flow<String> =
wablePreferencesDataSource.refreshToken

override fun getAutoLogin(): Flow<Boolean> =
wablePreferencesDataSource.autoLogin

override fun getNickname(): Flow<String> =
wablePreferencesDataSource.nickname

override fun getMemberId(): Flow<Int> =
wablePreferencesDataSource.memberId

override fun getMemberProfileUrl(): Flow<String> =
wablePreferencesDataSource.memberProfileUrl

override fun getIsPushAlarmAllowed(): Flow<Boolean> =
wablePreferencesDataSource.isPushAlarmAllowed

override suspend fun saveAccessToken(accessToken: String) {
wablePreferencesDataSource.updateAccessToken(accessToken)
}

override suspend fun saveRefreshToken(refreshToken: String) {
wablePreferencesDataSource.updateRefreshToken(refreshToken)
}

override suspend fun saveAutoLogin(autoLogin: Boolean) {
wablePreferencesDataSource.updateAutoLogin(autoLogin)
}

override suspend fun saveNickname(name: String) {
wablePreferencesDataSource.updateNickname(name)
}

override suspend fun saveMemberId(memberId: Int) {
wablePreferencesDataSource.updateMemberId(memberId)
}

override suspend fun saveMemberProfileUrl(memberUrl: String) {
wablePreferencesDataSource.updateMemberProfileUrl(memberUrl)
}

override suspend fun saveIsPushAlarmAllowed(isPushAlarmAllowed: Boolean) {
wablePreferencesDataSource.updateIsPushAlarmAllowed(isPushAlarmAllowed)
}

override suspend fun clearAll() {
wablePreferencesDataSource.clear()
}

override suspend fun clearForRefreshToken() {
wablePreferencesDataSource.clearForRefreshToken()
}
}
1 change: 1 addition & 0 deletions core/datastore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
13 changes: 13 additions & 0 deletions core/datastore/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
alias(libs.plugins.android.library)
id("com.teamwable.wable.kotlin")
id("com.teamwable.wable.hilt")
}

android {
namespace = "com.teamwable.datastore"
}

dependencies {
implementation(libs.bundles.datastore)
}
4 changes: 4 additions & 0 deletions core/datastore/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.teamwable.datastore.datasource

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException
import javax.inject.Inject

class DefaultWablePreferenceDatasource @Inject constructor(
private val dataStore: DataStore<Preferences>,
) : WablePreferencesDataSource {
private object PreferencesKeys {
val AccessToken = stringPreferencesKey("accessToken")
val RefreshToken = stringPreferencesKey("refreshToken")
val Nickname = stringPreferencesKey("nickname")
val AutoLogin = booleanPreferencesKey("autoLogin")
val MemberId = intPreferencesKey("memberId")
val MemberProfileUrl = stringPreferencesKey("memberProfileUrl")
val IsPushAlarmAllowed = booleanPreferencesKey("isPushAlarmAllowed")
}

override val accessToken: Flow<String> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.AccessToken].orEmpty()
}

override val refreshToken: Flow<String> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.RefreshToken].orEmpty()
}

override val autoLogin: Flow<Boolean> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.AutoLogin] ?: false
}

override val nickname: Flow<String> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.Nickname].orEmpty()
}

override val memberId: Flow<Int> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.MemberId] ?: -1
}

override var memberProfileUrl: Flow<String> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.MemberProfileUrl].orEmpty()
}

override var isPushAlarmAllowed: Flow<Boolean> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.IsPushAlarmAllowed] ?: false
}

override suspend fun updateAccessToken(accessToken: String) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.AccessToken] = accessToken
}
}

override suspend fun updateRefreshToken(refreshToken: String) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.RefreshToken] = refreshToken
}
}

override suspend fun updateAutoLogin(autoLogin: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.AutoLogin] = autoLogin
}
}

override suspend fun updateNickname(name: String) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.Nickname] = name
}
}

override suspend fun updateMemberId(memberId: Int) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.MemberId] = memberId
}
}

override suspend fun updateMemberProfileUrl(memberUrl: String) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.MemberProfileUrl] = memberUrl
}
}

override suspend fun updateIsPushAlarmAllowed(isPushAlarmAllowed: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.IsPushAlarmAllowed] = isPushAlarmAllowed
}
}

override suspend fun clear() {
dataStore.edit { preferences ->
preferences.clear()
}
}

override suspend fun clearForRefreshToken() {
dataStore.edit { preferences ->
preferences.remove(PreferencesKeys.AccessToken)
preferences.remove(PreferencesKeys.RefreshToken)
preferences.remove(PreferencesKeys.AutoLogin)
}
}
}

private suspend fun FlowCollector<Preferences>.handleError(it: Throwable) {
if (it is IOException) emit(emptyPreferences())
else throw it
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 throw된 exception은 어디서 핸들링 되나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공식문서 복붙 해온거라 그 생각은 못해봤네요!
이 예외는 상위에서 에러핸들링 하라는 의도인것 같습니다.

해당 flow를 viewmodel이나 activity에서 collect하기전 catch문을 통해서 error를 로깅을 찍거나 할수는 있을 것 같아요!
모든 datastore.data에 각 타입별로 default값을 할당 해놓아서 실패시에도 값이 map이 되긴 합니다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.teamwable.datastore.datasource

import kotlinx.coroutines.flow.Flow

interface WablePreferencesDataSource {
val accessToken: Flow<String>
val refreshToken: Flow<String>
val autoLogin: Flow<Boolean>
val nickname: Flow<String>
val memberId: Flow<Int>
val memberProfileUrl: Flow<String>
val isPushAlarmAllowed: Flow<Boolean>

suspend fun updateAccessToken(accessToken: String)

suspend fun updateRefreshToken(refreshToken: String)

suspend fun updateAutoLogin(autoLogin: Boolean)

suspend fun updateNickname(name: String)

suspend fun updateMemberId(memberId: Int)

suspend fun updateMemberProfileUrl(memberUrl: String)

suspend fun updateIsPushAlarmAllowed(isPushAlarmAllowed: Boolean)

suspend fun clear()

suspend fun clearForRefreshToken()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.teamwable.datastore.di

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {
private val Context.dataStore by preferencesDataStore(name = "wable_data_store")

@Singleton
@Provides
fun provideDataStore(
@ApplicationContext context: Context,
): DataStore<Preferences> =
context.dataStore
}
2 changes: 1 addition & 1 deletion core/network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ android {
buildFeatures.apply {
buildConfig = true
}

}

dependencies {
implementation(project(":core:model"))
implementation(project(":core:datastore"))
}
Loading