-
-
Notifications
You must be signed in to change notification settings - Fork 2k
kotlin_multiplatform
MMKV provides experimental Kotlin Multiplatform support for Android and iOS starting from v2.4.1. The KMP API and published artifact layout may change in a future release.
The KMP wrapper exposes the same MMKV files and native implementation as the platform SDKs while allowing storage calls to live in shared Kotlin code.
The v2.4.1 package publishes these targets:
- Android (API level 23 or later, 64-bit ABIs)
-
iosArm64(iOS 13.0 or later) -
iosSimulatorArm64(iOS 14.0 or later) -
iosX64(iOS 13.0 or later)
Android projects must have AndroidX enabled:
android.useAndroidX=trueMake sure Maven Central is available to dependency resolution:
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}Add the root KMP package to the shared module:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.tencent:mmkv-kmp:2.4.1")
}
}
}Depend only on com.tencent:mmkv-kmp:2.4.1. Gradle metadata selects the correct target artifact automatically:
| Target | Selected artifact |
|---|---|
| Common metadata | com.tencent:mmkv-kmp:2.4.1 |
| Android | com.tencent:mmkv-kmp-android:2.4.1 |
| iOS device | com.tencent:mmkv-kmp-iosarm64:2.4.1 |
| Apple Silicon iOS simulator | com.tencent:mmkv-kmp-iossimulatorarm64:2.4.1 |
| Intel iOS simulator | com.tencent:mmkv-kmp-iosx64:2.4.1 |
Do not add the target-specific artifacts directly.
Initialization is platform-specific and must happen before any shared MMKV code runs. Import com.tencent.mmkv.kmp.initialize in the platform source set.
Initialize MMKV from your Application or another startup entry point:
import android.app.Application
import com.tencent.mmkv.kmp.MMKV
import com.tencent.mmkv.kmp.initialize
class App : Application() {
override fun onCreate() {
super.onCreate()
val rootDir = MMKV.initialize(this)
println("MMKV root: $rootDir")
}
}Register the Application class in AndroidManifest.xml when your project does not already have one:
<application
android:name=".App"
... />To use a custom root directory:
MMKV.initialize(applicationContext, rootDir = customRootDir)The overload also accepts logLevel and an MMKVHandler.
Initialize MMKV on the main thread from the iOS startup path:
import com.tencent.mmkv.kmp.MMKV
import com.tencent.mmkv.kmp.initialize
fun initializeMMKV() {
val rootDir = MMKV.initialize()
println("MMKV root: $rootDir")
}The default root is the app's Documents directory under mmkv. A custom root can be supplied with MMKV.initialize(rootDir = customRootDir).
For multi-process access between an app and its extensions, enable the same App Group entitlement on every target and initialize with its container:
import platform.Foundation.NSFileManager
val groupDir = NSFileManager.defaultManager
.containerURLForSecurityApplicationGroupIdentifier("group.example.app")
?.path
checkNotNull(groupDir)
MMKV.initialize(groupDir = groupDir)Every participating process must initialize MMKV with the same App Group container.
After platform initialization, MMKV operations can live in commonMain:
import com.tencent.mmkv.kmp.MMKV
val kv = MMKV.defaultMMKV()
kv.encodeBool("bool", true)
val boolValue = kv.decodeBool("bool")
kv.encodeInt("int", Int.MIN_VALUE)
val intValue = kv.decodeInt("int")
kv.encodeLong("long", Long.MAX_VALUE)
val longValue = kv.decodeLong("long")
kv.encodeDouble("double", 3.14)
val doubleValue = kv.decodeDouble("double")
kv.encodeString("string", "Hello from MMKV KMP")
val stringValue = kv.decodeString("string")
kv.encodeBytes("bytes", "Hello".encodeToByteArray())
val bytesValue = kv.decodeBytes("bytes")Supported value types are Boolean, Int, Long, Float, Double, String, and ByteArray.
val kv = MMKV.defaultMMKV()
if (kv.containsKey("string")) {
kv.removeValueForKey("string")
}
kv.removeValuesForKeys(listOf("int", "long"))
println("keys: ${kv.allKeys}")
println("count: ${kv.count}")
kv.clearAll()Use a unique ID to isolate data:
val kv = MMKV.mmkvWithID("account")Use MMKVConfig for all instance options:
import com.tencent.mmkv.kmp.MMKVConfig
import com.tencent.mmkv.kmp.MMKVMode
val config = MMKVConfig(
mode = MMKVMode.MULTI_PROCESS,
cryptKey = "MyEncryptKey",
expectedCapacity = 8L * 1024,
enableCompareBeforeSet = true,
)
val kv = MMKV.mmkvWithID("shared-account", config)Available configuration fields include mode, encryption, AES-256, custom root path, expected capacity, expiration, compare-before-set, recovery strategy, and item-size limit.
Use MMKVMode.READ_ONLY for a read-only instance. Modes are flags and can be combined when needed:
val config = MMKVConfig(
mode = MMKVMode.MULTI_PROCESS or MMKVMode.READ_ONLY,
)
val readOnly = MMKV.mmkvWithID("shared-account", config)Configure an encrypted instance when creating it:
val encrypted = MMKV.mmkvWithID(
"secure",
MMKVConfig(
cryptKey = "MyEncryptKey",
aes256 = false,
),
)Use reKey() to add, replace, or remove encryption:
encrypted.reKey("Key_seq_1")
encrypted.reKey("Key_Seq_Very_Looooooooong", aes256 = true)
encrypted.reKey(null)Store encryption keys separately from the MMKV files. MMKV encryption does not replace platform key-management facilities.
Enable a default expiration duration for an instance:
import com.tencent.mmkv.kmp.MMKVExpireDuration
val kv = MMKV.mmkvWithID(
"cache",
MMKVConfig(
enableKeyExpire = true,
expiredInSeconds = MMKVExpireDuration.InDay,
),
)Override the duration for an individual value:
kv.encodeString("short-lived", "value", MMKVExpireDuration.InHour)
kv.encodeString("persistent", "value", MMKVExpireDuration.Never)Expiration durations are unsigned seconds. Enabling expiration changes the on-disk format and is not backward-compatible with MMKV v1.2.16 or earlier.
Use MMKVNameSpace to manage a group of instances under one root:
import com.tencent.mmkv.kmp.MMKVNameSpace
val namespace = MMKVNameSpace.of(customRootDir)
val kv = namespace.mmkvWithID("account")
kv.encodeString("name", "MMKV")The namespace also provides root-specific backupOneToDirectory(), restoreOneFromDirectory(), isFileValid(), checkExist(), and removeStorage() operations.
Back up or restore one instance:
val backedUp = MMKV.backupOneToDirectory(
mmapID = "account",
dstDir = backupDirectory,
)
val restored = MMKV.restoreOneFromDirectory(
mmapID = "account",
srcDir = backupDirectory,
)Back up or restore every instance in the default root:
val backupCount = MMKV.backupAllToDirectory(backupDirectory)
val restoreCount = MMKV.restoreAllFromDirectory(backupDirectory)Do not read or write an instance while restoring its files.
Register one common handler for recovery decisions, log redirection, content-change notifications, and successful-load notifications:
import com.tencent.mmkv.kmp.MMKVHandler
import com.tencent.mmkv.kmp.MMKVLogLevel
import com.tencent.mmkv.kmp.MMKVRecoverStrategic
val handler = object : MMKVHandler() {
override fun onMMKVCRCCheckFail(mmapID: String) =
MMKVRecoverStrategic.OnErrorRecover
override fun onMMKVFileLengthError(mmapID: String) =
MMKVRecoverStrategic.OnErrorRecover
override fun wantLogRedirect() = true
override fun mmkvLog(
level: MMKVLogLevel,
file: String,
line: Int,
function: String,
message: String,
) {
println("MMKV [$level] $message")
}
}
MMKV.registerHandler(handler)Call MMKV.unRegisterHandler() when the application no longer wants callbacks.
close() permanently destroys the native MMKV instance. All references backed by the same native instance become invalid immediately. The caller must ensure that no operation is running and no reference is used afterward. Discard every reference before reopening the same ID.
A second close() on the same KMP wrapper is harmless, and other calls on that closed wrapper fail. This does not make aliases safe: another wrapper backed by the same native instance is invalid after either wrapper closes it. Do not use close() as scoped ownership and do not retain aliases across it.
Most applications can keep MMKV instances for the process lifetime and do not need to call close(). MMKV.onExit() is also optional.
The Android target delegates to the native com.tencent:mmkv:2.4.1 AAR.
The iOS KLIBs embed MMKV Core through the C bridge. KMP consumers do not need CocoaPods, Swift Package Manager, or a source build for MMKV Core. Do not also link the native MMKV CocoaPod or SwiftPM product into the same iOS binary, because both contain MMKV Core and can produce duplicate native symbols.
-
Dependency cannot be resolved: Confirm that
mavenCentral()is available and that onlycom.tencent:mmkv-kmp:2.4.1is declared. -
AndroidX classes cannot be resolved: Add
android.useAndroidX=truetogradle.properties. - MMKV reports that it is not initialized: Run the platform-specific initializer before shared code creates an instance.
-
The iOS simulator target cannot link: Declare the target matching the host Mac:
iosSimulatorArm64on Apple Silicon oriosX64on Intel. - Duplicate MMKV symbols on iOS: Remove the separate native MMKV CocoaPods or SwiftPM dependency from that binary.
- An iOS extension cannot see the app's data: Configure matching App Group entitlements, pass the shared group container to initialization in every process, and open the instance in multi-process mode.
See the v2.4.1 change log for release details.
MMKV is published under the BSD 3-Clause license. For details check out the LICENSE.TXT.
Check out the CHANGELOG.md for details of change history.
If you are interested in contributing, check out the CONTRIBUTING.md, also join our Tencent OpenSource Plan.
To give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the Code of Conduct.
Check out the FAQ first. Should there be any questions, don't hesitate to create issues.
User privacy is taken very seriously: MMKV does not obtain, collect or upload any personal information. Please refer to the MMKV SDK Personal Information Protection Rules for details.
- In English
- 中文
- In English
- 中文
- In English
- 中文
-
In English
-
中文
-
Golang
- In English
- 中文