Jetpack DataStore with Kotlinx Serialization

이태훈·2022년 3월 29일
1

안녕하세요. 이번 포스팅에서는 kotlinx의 serialization을 통해 datastore에 정보를 저장하고 꺼내 써보는 방법에 대해 작성해보겠습니다.

기존 DataStore를 사용하는 방법은 Preferences DataStore, Proto DataStore 두 가지 방식이 있었습니다.

이 방식은 Proto DataStore 방식과 유사하며, 보일러 플레이트 코드들을 깔끔하게 할 수 있는 장점이 있습니다.

먼저, kotlinx의 serialization을 사용하기 위해 dependency를 설정해줍니다.

project/build.gradle
dependencies {
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}


module/build.gradle
plugins {
	id "kotlin"
	id "org.jetbrains.kotlin.plugin.serialization"
}

dependencies {
	implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.3.2"
}

먼저, 프로젝트 최상단 gradle과 모듈 gradle에 디펜던시를 설정해줍니다.

그런 다음, 사용할 정보에 Serilization을 적용해줍니다

import kotlinx.serialization.Serializable

@Serializable
data class UserPreferences(
	val firstName: String,
    val lastName: String,
    val address: String
)

다음으로 Serializer를 설정해줍니다.

object UserPreferencesSerializer : Serializer<UserPreferences> {

	override val defaultValue: UserPreferences = UserPreferences()

	override suspend fun readFrom(input: InputStream): UserPreferences {
		try {
			return Json.decodeFromString(
				UserPreferences.serializer(), input.readBytes().decodeToString()
			)
		} catch (e: SerializationException) {
			throw CorruptionException("Unable to read UserPrefs", e)
		}
	}

	override suspend fun writeTo(t: UserPreferences, output: OutputStream) {
		output.write(
			Json.encodeToString(UserPreferences.serializer(), t).encodeToByteArray()
		)
	}
}

(Optional) Hilt를 사용하시는 분들은 모듈을 만들어 DataStore를 주입시켜주시면 되겠습니다.

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {

	@Provides
    @Singleton
    fun provideUserPrefsDataStore(
        @ApplicationContext context: Context
    ) : DataStore<UserPreferences> {
        return DataStoreFactory.create(
            serializer = UserPreferencesSerializer,
            produceFile = { context.dataStoreFile("user_prefs.json")
        )
    }
}

@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {
	
    @Provides
    @Singleton
    fun bindUserPrefsRepository(userRepository: UserRepositoryImpl): UserRepository
}

저는 DataStore를 Repository 패턴 안에 넣어서 사용해서 이렇게 사용하였고, 뷰모델이나 다른 곳에 넣어서 사용하실 분들은 이렇게 안 하셔도 됩니다.

(Optional) Repository, UseCases

class UserPrefsRepositoryImpl @Inject constructor(
	private val dataStore: DataStore<UserPreferences>
) : UserPrefsRepository {

	override fun getUserPrefs(): Flow<UserPreferences> = dataStore.data
    
    override suspend fun setFirstName(firstName: String) {
    	dataStore.updateData { it.copy(firstName = firstName) }
    }
    
    override suspend fun setLastName(lastName: String) {
    	dataStore.updateData { it.copy(lastName = lastName) }
    }
    
    override suspend fun setAddress(address: String) {
    	dataStore.updateData { it.copy(address = address) }
    }
}

class GetUserPrefsUseCase @Inject constructor(
	private val userPrefsRepository: UserPrefsRepository
) {
	
    operator fun invoke() = userPrefsRepository.getUserPrefs()
}

뷰모델에서 해당 데이터를 가져와보겠습니다.

@HiltViewModel
class ViewModel(
	private val getUserPrefsUseCase: GetUserPrefsUseCase
) : ViewModel() {

	val userInfo = getUserPrefesUseCase().stateIn(
    	viewModelScope, SharingStarted.WhileSubsribed(5000), null
    )
}
profile
https://www.linkedin.com/in/%ED%83%9C%ED%9B%88-%EC%9D%B4-7b9563237

0개의 댓글