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

Add SQLDelight #22

Merged
merged 13 commits into from
Dec 5, 2019
Merged

Add SQLDelight #22

merged 13 commits into from
Dec 5, 2019

Conversation

tonilopezmr
Copy link
Contributor

@tonilopezmr tonilopezmr commented Oct 11, 2019

🎩 What is the goal?

  • Add SQLDelight on Kotlin Multiplatform on Android and iOS
  • Add TTL Cache to store data on SQLite

📝 How is it being implemented?

  • Create a TTL small cache
  • Add preferences on Multiplatform using SharedPreferences for Android and NSUserDefaults for iOS to save time for cache.
  • Add SQLDelight Gradle plugin

Android:

Create Android driver and share it with the injector to initialize dependencies.

val androidSqliteDriver = AndroidSqliteDriver(GalleryDb.Schema, this, "gallerydb")

GalleryInjector().init(androidSqliteDriver, ...)

IOS:

We need to create the iOS SQLite driver on Kotlin in the iosMain module on common lib, which will create this method only accessible by swift or ios module in common lib.

@Suppress("unused")
fun defaultDriver(): SqlDriver = NativeSqliteDriver(GalleryDb.Schema, "gallerydb")

Initialize the injector with the iOS driver like we made it on Android.

GalleryInjector().invoke().doInit(driver: IosSqliteKt.defaultDriver(), ... )

💥 How can it be tested?

On it, this is a draft

@tonilopezmr tonilopezmr requested a review from a team October 11, 2019 17:21
Copy link
Contributor

@Serchinastico Serchinastico left a comment

Choose a reason for hiding this comment

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

Still a draft and most comments are about naming stuff, feature-wise this is awesome Toni 💯

import android.content.SharedPreferences
import java.util.Calendar

actual class TimeProvider(
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it really odd that a class called TimeProvider stores anything. Provider usually means that its gets you something. I'd separate this class into two, TimeProvider having a getCurrentTime and timeAgo methods and a TimeStorage that can persist times.

}

actual fun persistTime() {
sharedPreferences.edit().apply {
Copy link
Contributor

Choose a reason for hiding this comment

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

Using apply twice in the same method with such a different meaning hurts readability IMO. I'd use an preferences variable even though it's like ~9 more characters 😝

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the problem is duplicate names and readability, I'd use run instead apply 👍


private fun timeAgo(): Long {
val cal = Calendar.getInstance()
cal.set(1969, Calendar.JULY, 21)
Copy link
Contributor

Choose a reason for hiding this comment

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

?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🚀😉

}

private fun insert(photos: PhotoShot) {
photosQueries.transaction {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need a transaction for a single insertion?

Copy link
Contributor

Choose a reason for hiding this comment

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

Same for the deletion below

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the number of insertions isn't related to the transaction right?, I mean if some other thread, try to insert in the same time, thanks to use transaction will be done in the right way if I am not wrong, it's not about things you are doing inside of a transaction is related to the possible calls from outside.

expect class TimeProvider {

fun persistTime()
fun getPersistedTime(): Long
Copy link
Contributor

Choose a reason for hiding this comment

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

When talking about time I'd either use stronger typing like Duration or specify the unit in the name: getPersistedTimeInMs

Copy link
Contributor Author

@tonilopezmr tonilopezmr Oct 11, 2019

Choose a reason for hiding this comment

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

I thought I couldn't use Duration in kotlin native 🤔, but I can use it like getCurrentTime().toDuration(DurationUnit.MILLISECONDS)

Also instead to use getPersistedTimeInMs it can be renamed to getPersistedTimestamp() because It will be a Long, except if you are working on python or swift xD

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Finally, I changed it with getPersistedTimeInMs

class LocalPhotosDataSource(private val cache: TTLCache) {

fun insert(photos: Photos) {
photos.map(::insert)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a map but a forEach

Suggested change
photos.map(::insert)
photos.forEach(::insert)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😅

Comment on lines +28 to +38
private suspend fun getPhotos(): Photos =
if (localPhotosDataSource.isExpired()) {
logInfo(TAG, "Getting from internet")
photosApiClient.getPhotos().also { photos ->
localPhotosDataSource.removeAll()
localPhotosDataSource.insert(photos)
}
} else {
logInfo(TAG, "Getting from local database")
localPhotosDataSource.getPhotos()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Great 👏


companion object {
private const val TIME_ARG = "time_arg"
private const val THREE_MONTHS_IN_MILLIS = 259200
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private const val THREE_MONTHS_IN_MILLIS = 259200
private val THREE_MONTHS_IN_MILLIS = 90.days.toLongMilliseconds()

Copy link
Contributor

Choose a reason for hiding this comment

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

Now that the specific time is encoded into the value we can rename the variable to its actual meaning like TIME_IN_MS_TO_EXPIRE_DATA or something like that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I think either is fine, three months in millis is the number and time in ms to expire data is the purpose of that number.

But I forgot to use the kotlin.time the approach I used for the TTLCache -.-, good catch 💪

private const val THREE_MONTHS_IN_MILLIS = 259200
}

private val delegate: NSUserDefaults = NSUserDefaults.standardUserDefaults
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does the delegate name comes from? In iOS a delegate is just a listener and it doesn't look like we are using that pattern in here. What about just calling it userDefaults?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know too much in iOS and I guess it was something that it's used in iOS development, I'll change it! 👍

Comment on lines +28 to +32
val timeVal = alloc<timeval>()
gettimeofday(timeVal.ptr, null)
val sec = timeVal.tv_sec
val usec = timeVal.tv_usec
((sec * 1_000L) + (usec / 1_000L))
Copy link
Contributor

Choose a reason for hiding this comment

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

Fancy 🤓

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I spent too much time to find how to get a long timestamp on swift, it returns Double -.- with NSDate.timeIntervalSince1970

@tonilopezmr tonilopezmr marked this pull request as ready for review December 5, 2019 13:01
@tonilopezmr tonilopezmr merged commit fb6a0ab into master Dec 5, 2019
@tonilopezmr tonilopezmr deleted the add-sqldelight branch December 5, 2019 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants