onceKmp is a Kotlin Multiplatform library inspired by jonfinerty/Once, used to track one-off actions and rate-limited actions across app install, app version, and app session scopes.
onceKmp: publishable KMP library module.onceKmpSample: sample wrapper module showing real usage.
- Tag-based tracking:
markDone(tag),beenDone(...),lastDone(tag) - Scope-based checks:
Once.THIS_APP_INSTALLOnce.THIS_APP_VERSIONOnce.THIS_APP_SESSION
- Time-window checks:
beenDone(OnceTimeUnit, amount, tag)beenDone(timeSpanInMillis, tag)
- Count checks:
Amount.exactly(n)Amount.moreThan(n)Amount.lessThan(n)
- To-do workflow:
toDo(...)needToDo(tag)clearToDo(tag)
- Group:
io.github.mrjoechen - Artifact:
oncekmp - Kotlin package:
io.github.mrjoechen - Android namespace:
io.github.mrjoechen
kotlin { sourceSets { commonMain.dependencies { implementation("io.github.mrjoechen:oncekmp:<latest-version>") } } }settings.gradle.kts:
include(":onceKmp")Consumer module:
dependencies { implementation(project(":onceKmp")) }Unified call for Android / iOS / Desktop:
Once.initialise()Android explicit fallback:
Once.initialise(applicationContext)Desktop/iOS custom app update timestamp:
Once.initialise(appUpdatedTimeMillis = 1_700_000_000_000L)Desktop custom storage directory (optional):
Once.initialise( storageDir = java.nio.file.Paths.get("/custom/path/for/oncekmp") )if (!Once.beenDone(Once.THIS_APP_VERSION, "show_whats_new")) { // show what's new Once.markDone("show_whats_new") }Once uses two persisted stores and one in-memory session list:
- Persisted map store name:
PersistedMapTagLastSeenMap- key:
tag - value: list of done timestamps (
Long) serialized as comma-separated text in storage adapters.
- key:
- Persisted set store name:
PersistedSetToDoSet- key:
PersistedSetValues - value: to-do tag set.
- key:
- In-memory only: session tag list for
THIS_APP_SESSIONchecks.
- Storage API:
Context.getSharedPreferences(name, Context.MODE_PRIVATE)SharedPreferencesread/write viagetAll()/putString()/putStringSet()/remove()/clear()
- App version timestamp API:
PackageManager.getPackageInfo(...).lastUpdateTime
- Default storage files:
/data/data/<applicationId>/shared_prefs/PersistedMapTagLastSeenMap.xml/data/data/<applicationId>/shared_prefs/PersistedSetToDoSet.xml
- Auto init context capture:
OnceContextProvideris declared in library manifest and cachesapplicationContext.
- Storage API:
NSUserDefaults.standardUserDefaultsstringForKey/objectForKey/arrayForKey/setObject/removeObjectForKey/dictionaryRepresentation
- Key namespace rule:
<storeName>:<key>- example:
PersistedMapTagLastSeenMap:show_whats_new
- Physical storage (Apple managed plist):
- device:
/var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/<bundle-id>.plist - simulator:
~/Library/Developer/CoreSimulator/Devices/<UDID>/data/Containers/Data/Application/<UUID>/Library/Preferences/<bundle-id>.plist
- device:
- Storage API:
java.nio.file+java.util.Properties- each store is written as
<storeName>.propertiesfile
- Default root node (auto-detected):
- priority:
oncekmp.desktop.appIdsystem property - then:
app.id/application.id/app.identifier/bundle.id/app.name - then: runtime identity (
jpackage.app-path/sun.java.command/java.class.path) - final fallback:
oncekmp-app-<working-directory-name>
- priority:
- Default storage directory:
- macOS:
~/Library/Application Support/<rootNode>/oncekmp/ - Linux:
${XDG_CONFIG_HOME:-~/.config}/<rootNode>/oncekmp/ - Windows:
%APPDATA%\\<rootNode>\\oncekmp\\
- macOS:
- Notes:
- different apps are isolated by different
<rootNode> - uninstalling an app does not always guarantee removal of user config data on all desktop installers
- deleting the app-specific data directory always clears
oncekmpstate
- different apps are isolated by different