├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .gitignore
├── settings.gradle.kts
├── mmkv-kotlin
├── src
│ ├── androidInstrumentedTest
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── ctrip
│ │ │ └── flight
│ │ │ └── mmkv
│ │ │ ├── MMKVTestService.kt
│ │ │ └── MMKVKMMTestAndroid.kt
│ ├── commonMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── ctrip
│ │ │ └── flight
│ │ │ └── mmkv
│ │ │ ├── MMKVMode.kt
│ │ │ ├── Extension.kt
│ │ │ ├── MMKVLogLevel.kt
│ │ │ ├── Util.kt
│ │ │ ├── Creator.kt
│ │ │ └── MMKV_KMP.kt
│ ├── androidMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── ctrip
│ │ │ └── flight
│ │ │ └── mmkv
│ │ │ ├── MMKVMode.kt
│ │ │ ├── UtilAndroid.kt
│ │ │ ├── ExtensionAndroid.kt
│ │ │ ├── MMKVLogLevel.kt
│ │ │ ├── CreatorAndroid.kt
│ │ │ ├── InitializerAndroid.kt
│ │ │ └── MMKVImpl.kt
│ ├── appleMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── ctrip
│ │ │ └── flight
│ │ │ └── mmkv
│ │ │ ├── MMKVMode.kt
│ │ │ ├── MMKVLogLevel.kt
│ │ │ ├── InitializeIos.kt
│ │ │ ├── UtilIos.kt
│ │ │ ├── Binary.kt
│ │ │ ├── ExtensionIos.kt
│ │ │ ├── CreatorIos.kt
│ │ │ └── MMKVImpl.kt
│ ├── appleTest
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── ctrip
│ │ │ └── flight
│ │ │ └── mmkv
│ │ │ └── MMKVKMMTestIos.kt
│ └── commonTest
│ │ └── kotlin
│ │ └── com
│ │ └── ctrip
│ │ └── flight
│ │ └── mmkv
│ │ └── MMKVKMMTest.kt
├── MMKV_Kotlin.podspec
└── build.gradle.kts
├── gradle.properties
├── .github
└── workflows
│ ├── publish.yml
│ └── build.yml
├── gradlew.bat
├── CHANGELOG.md
├── README_CN.md
├── gradlew
├── README.md
└── LICENSE.txt
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctripcorp/mmkv-kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | /local.properties
5 | /.idea
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .cxx
11 | local.properties
12 | /MMKV-Kotlin/build
13 | .podspec
14 | .kotlin
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Feb 22 11:03:04 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "mmkv-kotlin"
2 | include(":mmkv-kotlin")
3 |
4 | pluginManagement {
5 | repositories {
6 | google()
7 | gradlePluginPortal()
8 | mavenCentral()
9 | }
10 | }
11 |
12 | dependencyResolutionManagement {
13 | @Suppress("UnstableApiUsage")
14 | repositories {
15 | google()
16 | mavenCentral()
17 | }
18 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidInstrumentedTest/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Gradle
2 | org.gradle.daemon=true
3 | org.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djdk.attach.allowAttachSelf=true
4 | org.gradle.configureondemand=true
5 | org.gradle.workers.max=16
6 | org.gradle.parallel=true
7 | org.gradle.caching=true
8 |
9 | #Android
10 | android.useAndroidX=true
11 | android.enableJetifier=true
12 |
13 | #Kotlin
14 | kotlin.code.style=official
15 | kotlin.mpp.stability.nowarn=true
16 | kotlin.mpp.enableCInteropCommonization=true
17 | kotlin.native.increment=true
18 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/MMKVMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * Common MMKV model
21 | * @author yaqiao
22 | */
23 |
24 | expect enum class MMKVMode {
25 | SINGLE_PROCESS,
26 | MULTI_PROCESS,
27 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/Extension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * MMKV common extension
21 | * @author yaqiao
22 | */
23 |
24 | inline fun MMKV_KMP.withOpen(block: MMKV_KMP.() -> T): T = try {
25 | block()
26 | } finally {
27 | close()
28 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/MMKVLogLevel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * Common log level
21 | * @author yaqiao
22 | */
23 |
24 | expect enum class MMKVLogLevel {
25 | LevelDebug,
26 | LevelInfo,
27 | LevelWarning,
28 | LevelError,
29 | LevelNone;
30 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/MMKVMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * MMKV model Android actual
21 | * @author yaqiao
22 | */
23 |
24 | actual enum class MMKVMode {
25 | SINGLE_PROCESS {
26 | override val rawValue: Int = 1
27 | },
28 | MULTI_PROCESS {
29 | override val rawValue: Int = 2
30 | };
31 |
32 | abstract val rawValue: Int
33 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/MMKVMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * MMKV model iOS actual
21 | * @author yaqiao
22 | */
23 |
24 | actual enum class MMKVMode {
25 | SINGLE_PROCESS {
26 | override val rawValue: ULong = 0x1u
27 | },
28 | MULTI_PROCESS {
29 | override val rawValue: ULong = 0x2u
30 | };
31 |
32 | abstract val rawValue: ULong
33 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/Util.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | package com.ctrip.flight.mmkv
19 |
20 | /**
21 | * Other top-level function
22 | * @author yaqiao
23 | */
24 |
25 | expect fun backupOneToDirectory(mmapID: String, dstDir: String, rootPath: String?): Boolean
26 |
27 | expect fun pageSize(): Long
28 |
29 | expect fun setLogLevel(logLevel: MMKVLogLevel)
30 |
31 | expect fun version(): String
32 |
33 | expect fun unregisterHandler()
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/Creator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * MMKV create function common expect
21 | * @author yaqiao
22 | */
23 |
24 | expect fun defaultMMKV(): MMKV_KMP
25 |
26 | expect fun defaultMMKV(cryptKey: String): MMKV_KMP
27 |
28 | expect fun mmkvWithID(
29 | mmapId: String,
30 | mode: MMKVMode = MMKVMode.SINGLE_PROCESS,
31 | cryptKey: String? = null,
32 | rootPath: String? = null,
33 | ): MMKV_KMP
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 |
3 | kotlin = "2.2.21"
4 | agp = "8.12.3"
5 | mmkv = "2.2.4"
6 | junit = "4.13.2"
7 | androidx-test = "1.7.0"
8 | androidx-test-runner = "1.7.0"
9 | vanniktech-maven-publish = "0.34.0"
10 |
11 | [libraries]
12 |
13 | mmkv = { group = "com.tencent", name = "mmkv", version.ref = "mmkv" }
14 | junit = { group = "junit", name = "junit", version.ref = "junit" }
15 | androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidx-test" }
16 | androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidx-test-runner" }
17 | androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidx-test" }
18 |
19 | [plugins]
20 |
21 | multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
22 | cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" }
23 | kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize" }
24 | android-library = { id = "com.android.library", version.ref = "agp" }
25 | vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "vanniktech-maven-publish" }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/MMKVLogLevel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * Log level iOS actual
21 | * @author yaqiao
22 | */
23 |
24 | actual enum class MMKVLogLevel {
25 | LevelDebug {
26 | override val rawValue: ULong = 0u
27 | },
28 | LevelInfo {
29 | override val rawValue: ULong = 1u
30 | },
31 | LevelWarning {
32 | override val rawValue: ULong = 2u
33 | },
34 | LevelError {
35 | override val rawValue: ULong = 3u
36 | },
37 | LevelNone {
38 | override val rawValue: ULong = 4u
39 | };
40 |
41 | abstract val rawValue: ULong
42 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/UtilAndroid.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import com.tencent.mmkv.MMKV
20 |
21 | /**
22 | * Other top-level function
23 | * @author yaqiao
24 | */
25 |
26 | actual fun backupOneToDirectory(
27 | mmapID: String, dstDir: String, rootPath: String?
28 | ): Boolean = MMKV.backupOneToDirectory(mmapID, dstDir, rootPath)
29 |
30 | actual fun pageSize(): Long = MMKV.pageSize().toLong()
31 |
32 | actual fun setLogLevel(logLevel: MMKVLogLevel) = MMKV.setLogLevel(logLevel.rawValue)
33 |
34 | actual fun version(): String = MMKV.version()
35 |
36 | actual fun unregisterHandler() = MMKV.unregisterHandler()
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/InitializeIos.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:OptIn(ExperimentalForeignApi::class)
18 |
19 | package com.ctrip.flight.mmkv
20 |
21 | import cocoapods.MMKV.MMKV
22 | import kotlinx.cinterop.ExperimentalForeignApi
23 |
24 | /**
25 | * iOS MMKV initialize function
26 | * @author yaqiao
27 | */
28 |
29 | fun initialize(rootDir: String? = null): String = MMKV.initializeMMKV(rootDir)
30 |
31 | fun initialize(rootDir: String? = null, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, logLevel.rawValue)
32 |
33 | fun initialize(rootDir: String? = null, groupDir: String, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, groupDir, logLevel.rawValue)
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/UtilIos.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:OptIn(ExperimentalForeignApi::class)
18 |
19 | package com.ctrip.flight.mmkv
20 |
21 | import cocoapods.MMKV.MMKV
22 | import kotlinx.cinterop.ExperimentalForeignApi
23 |
24 | /**
25 | * Other top-level functions
26 | * @author yaqiao
27 | */
28 |
29 | actual fun backupOneToDirectory(
30 | mmapID: String, dstDir: String, rootPath: String?
31 | ): Boolean = MMKV.backupOneMMKV(mmapID, rootPath, dstDir)
32 |
33 | actual fun pageSize(): Long = MMKV.pageSize().toLong()
34 |
35 | actual fun setLogLevel(logLevel: MMKVLogLevel) = MMKV.setLogLevel(logLevel.rawValue)
36 |
37 | actual fun version(): String = MMKV.version()
38 |
39 | actual fun unregisterHandler() = MMKV.unregiserHandler()
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/Binary.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:OptIn(ExperimentalForeignApi::class)
18 |
19 | package com.ctrip.flight.mmkv
20 |
21 | import kotlinx.cinterop.*
22 | import platform.Foundation.NSData
23 | import platform.Foundation.create
24 | import platform.posix.memcpy
25 |
26 | /**
27 | * Between with Kotlin ByteArray and Objective-C NSData convert to each other
28 | * @author yaqiao
29 | */
30 |
31 | internal fun NSData.toByteArray(): ByteArray {
32 | val size = length.toInt()
33 | return ByteArray(size).apply {
34 | if (size > 0) usePinned {
35 | memcpy(it.addressOf(0), bytes, length)
36 | }
37 | }
38 | }
39 |
40 | @OptIn(BetaInteropApi::class)
41 | internal fun ByteArray.toNSData(): NSData = memScoped {
42 | NSData.create(bytes = allocArrayOf(this@toNSData),
43 | length = this@toNSData.size.toULong())
44 | }
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Build & Publish
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 |
8 | publish-mmkv-kotlin:
9 | runs-on: macos-15-intel
10 | timeout-minutes: 60
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v4
15 |
16 | - name: Validate Gradle Wrapper
17 | uses: gradle/wrapper-validation-action@v3
18 |
19 | - name: Set up JDK 21
20 | uses: actions/setup-java@v4
21 | with:
22 | distribution: 'zulu'
23 | java-version: 21
24 |
25 | - name: Setup Gradle
26 | uses: gradle/actions/setup-gradle@v4
27 |
28 | - name: Cache Build Tooling
29 | uses: actions/cache@v4
30 | with:
31 | path: |
32 | ~/.gradle/caches
33 | ~/.konan
34 | key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
35 |
36 | - name: Build All
37 | run: pod repo update --verbose && ./gradlew :mmkv-kotlin:assemble
38 |
39 | - name: Publish to MavenCentral
40 | run: ./gradlew :mmkv-kotlin:publishAllPublicationsToMavenCentralRepository
41 | env:
42 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
43 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
44 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
45 | ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
46 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
47 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/ExtensionAndroid.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import android.os.Parcelable
20 | import kotlin.reflect.KClass
21 |
22 | /**
23 | * MMKV Android extension
24 | * @author yaqiao
25 | */
26 |
27 | operator fun MMKVImpl.set(key: String, value: Parcelable?) = platformMMKV.encode(key, value)
28 |
29 | fun MMKVImpl.getParcelable(
30 | key: String,
31 | default: T? = null,
32 | clazz: KClass? = null,
33 | ): T? = platformMMKV.decodeParcelable(key, clazz?.java, default)
34 |
35 | @Deprecated(
36 | message = "Renamed to 'getParcelable' for clarity, as the 'take' prefix could be confusing.",
37 | replaceWith = ReplaceWith("getParcelable(key, default, clazz)")
38 | )
39 | fun MMKVImpl.takeParcelable(
40 | key: String,
41 | default: T? = null,
42 | clazz: KClass? = null,
43 | ): T? = getParcelable(key, default, clazz)
44 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/MMKVLogLevel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * Log level Android actual
21 | * @author yaqiao
22 | */
23 |
24 | typealias TencentMMKVLogLevel = com.tencent.mmkv.MMKVLogLevel
25 |
26 | actual enum class MMKVLogLevel {
27 | LevelDebug {
28 | override val rawValue: TencentMMKVLogLevel = TencentMMKVLogLevel.LevelDebug
29 | },
30 | LevelInfo {
31 | override val rawValue: TencentMMKVLogLevel = TencentMMKVLogLevel.LevelInfo
32 | },
33 | LevelWarning {
34 | override val rawValue: TencentMMKVLogLevel = TencentMMKVLogLevel.LevelWarning
35 | },
36 | LevelError {
37 | override val rawValue: TencentMMKVLogLevel = TencentMMKVLogLevel.LevelError
38 | },
39 | LevelNone {
40 | override val rawValue: TencentMMKVLogLevel = TencentMMKVLogLevel.LevelNone
41 | };
42 |
43 | abstract val rawValue: TencentMMKVLogLevel
44 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/CreatorAndroid.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import com.tencent.mmkv.MMKV
20 |
21 | /**
22 | * MMKV create function Android actual
23 | * @author yaqiao
24 | */
25 |
26 | actual fun defaultMMKV(): MMKV_KMP = MMKVImpl(MMKV.defaultMMKV())
27 |
28 | actual fun defaultMMKV(cryptKey: String): MMKV_KMP = MMKVImpl(MMKV.defaultMMKV(MMKV.SINGLE_PROCESS_MODE, cryptKey))
29 |
30 | @JvmOverloads
31 | actual fun mmkvWithID(
32 | mmapId: String,
33 | mode: MMKVMode,
34 | cryptKey: String?,
35 | rootPath: String?,
36 | ): MMKV_KMP = MMKVImpl(MMKV.mmkvWithID(mmapId, mode.rawValue, cryptKey, rootPath))
37 |
38 | fun mmkvWithAchemaID(
39 | mmapID: String,
40 | fd: Int,
41 | metaFD: Int,
42 | cryptKey: String?,
43 | ): MMKV_KMP = MMKVImpl(MMKV.mmkvWithAshmemFD(
44 | mmapID, fd, metaFD, cryptKey,
45 | ))
46 |
47 | fun backedUpMMKVWithID(
48 | mmapID: String,
49 | mode: MMKVMode,
50 | cryptKey: String?,
51 | rootPath: String,
52 | ): MMKV_KMP = MMKVImpl(MMKV.backedUpMMKVWithID(
53 | mmapID, mode.rawValue, cryptKey, rootPath,
54 | ))
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/InitializerAndroid.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import android.content.Context
20 | import com.tencent.mmkv.MMKV
21 |
22 | /**
23 | * Android MMKV initialize function
24 | * @author yaqiao
25 | */
26 |
27 | fun initialize(context: Context): String = MMKV.initialize(context)
28 |
29 | fun initialize(context: Context, rootDir: String): String = MMKV.initialize(context, rootDir)
30 |
31 | fun initialize(context: Context, loader: MMKV.LibLoader): String = MMKV.initialize(context, loader)
32 |
33 | fun initialize(context: Context, logLevel: MMKVLogLevel): String = MMKV.initialize(context, logLevel.rawValue)
34 |
35 | fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader): String = MMKV.initialize(context, rootDir, loader)
36 |
37 | fun initialize(context: Context, rootDir: String, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, logLevel.rawValue)
38 |
39 | fun initialize(context: Context, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, loader, logLevel.rawValue)
40 |
41 | fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, loader, logLevel.rawValue)
42 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/ExtensionIos.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:OptIn(ExperimentalForeignApi::class)
18 |
19 | package com.ctrip.flight.mmkv
20 |
21 | import kotlinx.cinterop.BetaInteropApi
22 | import kotlinx.cinterop.ExperimentalForeignApi
23 | import kotlinx.cinterop.ObjCClass
24 | import platform.Foundation.NSDate
25 | import platform.darwin.NSObject
26 |
27 | /**
28 | * MMKV iOS extension
29 | * @author yaqiao
30 | */
31 |
32 | operator fun MMKVImpl.set(key: String, value: NSDate) = platformMMKV.setDate(value, key)
33 |
34 | operator fun MMKVImpl.set(key: String, value: NSObject?) = platformMMKV.setObject(value, key)
35 |
36 | fun MMKVImpl.getNSDate(key: String, default: NSDate? = null): NSDate? = platformMMKV.getDateForKey(key, default)
37 |
38 | @OptIn(BetaInteropApi::class)
39 | fun MMKVImpl.getObject(key: String, cls: ObjCClass): Any? = platformMMKV.getObjectOfClass(cls, key)
40 |
41 | @Deprecated(
42 | message = "Renamed to 'getNSDate' for clarity, as the 'take' prefix could be confusing.",
43 | replaceWith = ReplaceWith("getNSDate(key, default)")
44 | )
45 | fun MMKVImpl.takeNSDate(key: String, default: NSDate? = null): NSDate? = getNSDate(key, default)
46 |
47 | @OptIn(BetaInteropApi::class)
48 | @Deprecated(
49 | message = "Renamed to 'getObject' for clarity, as the 'take' prefix could be confusing.",
50 | replaceWith = ReplaceWith("getObject(key, cls)")
51 | )
52 | fun MMKVImpl.takeObject(key: String, cls: ObjCClass): Any? = getObject(key, cls)
53 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidInstrumentedTest/kotlin/com/ctrip/flight/mmkv/MMKVTestService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import android.app.Service
20 | import android.content.Intent
21 | import android.os.IBinder
22 | import android.os.Process
23 |
24 | /**
25 | * MMKV multi processes tests Service
26 | * @author yaqiao
27 | */
28 |
29 | class MMKVTestService : Service() {
30 |
31 | companion object {
32 | const val SharedMMKVID = "SharedMMKVID"
33 | const val SharedMMKVKey = "SharedMMKVKey"
34 |
35 | const val CMD_Key = "CMD_Key"
36 | const val CMD_Update = "CMD_Update"
37 | const val CMD_Lock = "CMD_Lock"
38 | const val CMD_Kill = "CMD_Kill"
39 | }
40 |
41 | override fun onCreate() {
42 | super.onCreate()
43 | initialize(this)
44 | }
45 |
46 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
47 | val mmkv = mmkvWithID(SharedMMKVID, MMKVMode.MULTI_PROCESS) as? MMKVImpl ?: throw IllegalStateException("MMKV type has some problems")
48 | val cmd = intent.getStringExtra(CMD_Key)
49 | when (cmd) {
50 | CMD_Update -> {
51 | val value = mmkv.getInt(SharedMMKVKey)
52 | mmkv[SharedMMKVKey] = value + 1
53 | }
54 | CMD_Lock -> mmkv.lock()
55 | CMD_Kill -> stopSelf()
56 | }
57 | return START_NOT_STICKY
58 | }
59 |
60 | override fun onDestroy() {
61 | super.onDestroy()
62 | Process.killProcess(Process.myPid())
63 | }
64 |
65 | override fun onBind(intent: Intent?): IBinder? = null
66 |
67 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/CreatorIos.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import cocoapods.MMKV.MMKV
20 | import kotlinx.cinterop.ExperimentalForeignApi
21 | import platform.Foundation.NSData
22 |
23 | /**
24 | * MMKV create function iOS actual
25 | * @author yaqiao
26 | */
27 |
28 | private fun String.asNSDataCryptKey(): NSData = encodeToByteArray().toNSData()
29 |
30 | @OptIn(ExperimentalForeignApi::class)
31 | actual fun defaultMMKV(): MMKV_KMP = MMKVImpl(MMKV.defaultMMKV()!!)
32 |
33 | @OptIn(ExperimentalForeignApi::class)
34 | actual fun defaultMMKV(cryptKey: String): MMKV_KMP =
35 | MMKVImpl(MMKV.defaultMMKVWithCryptKey(cryptKey.asNSDataCryptKey())!!)
36 |
37 | @OptIn(ExperimentalForeignApi::class)
38 | actual fun mmkvWithID(
39 | mmapId: String,
40 | mode: MMKVMode,
41 | cryptKey: String?,
42 | rootPath: String?,
43 | ): MMKV_KMP {
44 | val platformMMKV = when {
45 | cryptKey != null && rootPath != null -> MMKV.mmkvWithID(
46 | mmapId,
47 | cryptKey.asNSDataCryptKey(),
48 | rootPath = rootPath,
49 | )
50 | cryptKey == null && rootPath != null -> MMKV.mmkvWithID(
51 | mmapId,
52 | rootPath = rootPath,
53 | )
54 | cryptKey != null && rootPath == null -> MMKV.mmkvWithID(
55 | mmapId,
56 | cryptKey.asNSDataCryptKey(),
57 | )
58 | cryptKey == null && rootPath == null -> MMKV.mmkvWithID(
59 | mmapId,
60 | mode = mode.rawValue.toULong(),
61 | )
62 | else -> throw IllegalStateException("Impossible situation")
63 | }!!
64 | return MMKVImpl(platformMMKV)
65 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/MMKV_Kotlin.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'mmkv_kotlin'
3 | spec.version = '1.3.1'
4 | spec.homepage = 'Link to the Shared Module homepage'
5 | spec.source = { :http=> ''}
6 | spec.authors = ''
7 | spec.license = ''
8 | spec.summary = 'Some description for the Shared Module'
9 | spec.vendored_frameworks = 'build/cocoapods/framework/MMKV_Kotlin.framework'
10 | spec.libraries = 'c++'
11 | spec.ios.deployment_target = '13.0'
12 | spec.osx.deployment_target = '10.15'
13 | spec.dependency 'MMKV', '2.2.4'
14 |
15 | if !Dir.exist?('build/cocoapods/framework/MMKV_Kotlin.framework') || Dir.empty?('build/cocoapods/framework/MMKV_Kotlin.framework')
16 | raise "
17 |
18 | Kotlin framework 'MMKV_Kotlin' doesn't exist yet, so a proper Xcode project can't be generated.
19 | 'pod install' should be executed after running ':generateDummyFramework' Gradle task:
20 |
21 | ./gradlew :mmkv-kotlin:generateDummyFramework
22 |
23 | Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)"
24 | end
25 |
26 | spec.xcconfig = {
27 | 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO',
28 | }
29 |
30 | spec.pod_target_xcconfig = {
31 | 'KOTLIN_PROJECT_PATH' => ':mmkv-kotlin',
32 | 'PRODUCT_MODULE_NAME' => 'MMKV_Kotlin',
33 | }
34 |
35 | spec.script_phases = [
36 | {
37 | :name => 'Build mmkv_kotlin',
38 | :execution_position => :before_compile,
39 | :shell_path => '/bin/sh',
40 | :script => <<-SCRIPT
41 | if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then
42 | echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\""
43 | exit 0
44 | fi
45 | set -ev
46 | REPO_ROOT="$PODS_TARGET_SRCROOT"
47 | "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \
48 | -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \
49 | -Pkotlin.native.cocoapods.archs="$ARCHS" \
50 | -Pkotlin.native.cocoapods.configuration="$CONFIGURATION"
51 | SCRIPT
52 | }
53 | ]
54 |
55 | end
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build & Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | pull_request:
8 | branches:
9 | - '*'
10 |
11 | jobs:
12 |
13 | build-mmkv-kotlin:
14 | runs-on: macos-15-intel
15 | timeout-minutes: 60
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v4
20 |
21 | - name: Validate Gradle Wrapper
22 | uses: gradle/wrapper-validation-action@v3
23 |
24 | - name: Set up JDK 21
25 | uses: actions/setup-java@v4
26 | with:
27 | distribution: 'zulu'
28 | java-version: 21
29 |
30 | - name: Setup Gradle
31 | uses: gradle/actions/setup-gradle@v4
32 |
33 | - name: Cache Build Tooling
34 | uses: actions/cache@v4
35 | with:
36 | path: |
37 | ~/.gradle/caches
38 | ~/.konan
39 | key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
40 |
41 | - name: Build All
42 | run: pod repo update --verbose && ./gradlew :mmkv-kotlin:assemble
43 |
44 | - name: Run Native Tests
45 | run: ./gradlew :mmkv-kotlin:cleanMacosX64Test && ./gradlew :mmkv-kotlin:macosX64Test --stacktrace
46 |
47 | - name: AVD Cache
48 | uses: actions/cache@v4
49 | id: avd-cache
50 | with:
51 | path: |
52 | ~/.android/avd/*
53 | ~/.android/adb*
54 | key: avd-36
55 |
56 | - name: Create AVD and Generate Snapshot for Caching
57 | if: steps.avd-cache.outputs.cache-hit != 'true'
58 | uses: reactivecircus/android-emulator-runner@v2
59 | with:
60 | api-level: 36
61 | target: google_apis
62 | arch: x86_64
63 | profile: pixel_6
64 | emulator-build: 14257411
65 | force-avd-creation: false
66 | emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
67 | disable-animations: true
68 | script: echo "Generated AVD snapshot for caching."
69 |
70 | - name: Run Android 16 Instrumented Tests
71 | uses: reactivecircus/android-emulator-runner@v2
72 | with:
73 | api-level: 36
74 | target: google_apis
75 | arch: x86_64
76 | profile: pixel_6
77 | emulator-build: 14257411
78 | force-avd-creation: false
79 | emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
80 | disable-animations: true
81 | script: ./gradlew :mmkv-kotlin:connectedDebugAndroidTest --stacktrace
82 |
83 | - name: Upload Reports
84 | uses: actions/upload-artifact@v4
85 | with:
86 | name: Test-Reports
87 | path: mmkv-kotlin/build/reports
88 | if: failure()
89 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/mmkv-kotlin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
2 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
3 |
4 | plugins {
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.cocoapods)
7 | alias(libs.plugins.android.library)
8 | alias(libs.plugins.kotlin.parcelize)
9 | alias(libs.plugins.vanniktech.maven.publish)
10 | }
11 |
12 | version = "1.3.1"
13 | group = "com.ctrip.flight.mmkv"
14 |
15 | @OptIn(ExperimentalKotlinGradlePluginApi::class)
16 | kotlin {
17 | jvmToolchain(21)
18 | androidTarget {
19 | publishLibraryVariants("release")
20 | instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test)
21 | }
22 | iosX64()
23 | iosArm64()
24 | iosSimulatorArm64()
25 | macosX64()
26 | macosArm64()
27 |
28 | compilerOptions {
29 | freeCompilerArgs.add("-Xexpect-actual-classes")
30 | }
31 |
32 | cocoapods {
33 | summary = "Some description for the Shared Module"
34 | homepage = "Link to the Shared Module homepage"
35 | ios.deploymentTarget = "13.0"
36 | osx.deploymentTarget = "10.15"
37 | framework {
38 | baseName = "MMKV-Kotlin"
39 | isStatic = true
40 | }
41 | pod(
42 | name = "MMKV",
43 | version = libs.versions.mmkv.get(),
44 | )
45 | }
46 |
47 | sourceSets {
48 | all {
49 | languageSettings.optIn("kotlin.RequiresOptIn")
50 | }
51 | commonTest.dependencies {
52 | implementation(kotlin("test"))
53 | }
54 | androidMain.dependencies {
55 | api(libs.mmkv)
56 | }
57 | androidInstrumentedTest.dependencies {
58 | implementation(kotlin("test-junit"))
59 | implementation(libs.junit)
60 | implementation(libs.androidx.test.core)
61 | implementation(libs.androidx.test.runner)
62 | implementation(libs.androidx.test.rules)
63 | }
64 | }
65 | }
66 |
67 | android {
68 | namespace = "com.ctrip.flight.mmkv"
69 | compileSdk = 36
70 | defaultConfig {
71 | minSdk = 23
72 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
73 | }
74 | }
75 |
76 | mavenPublishing {
77 | publishToMavenCentral()
78 | signAllPublications()
79 |
80 | coordinates(
81 | groupId = group.toString(),
82 | artifactId = "mmkv-kotlin",
83 | version = version.toString(),
84 | )
85 |
86 | pom {
87 | name.set("MMKV-Kotlin")
88 | description.set("MMKV for Kotlin Multiplatform")
89 | url.set("https://github.com/ctripcorp/mmkv-kotlin")
90 | licenses {
91 | license {
92 | name.set("The Apache License, Version 2.0")
93 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
94 | }
95 | }
96 | developers {
97 | developer {
98 | id.set("qiaoyuang")
99 | name.set("Yuang Qiao")
100 | email.set("qiaoyuang2012@gmail.com")
101 | }
102 | }
103 | scm {
104 | url.set("https://github.com/ctripcorp/mmkv-kotlin")
105 | connection.set("scm:git:https://github.com/ctripcorp/mmkv-kotlin.git")
106 | developerConnection.set("scm:git:https://github.com/ctripcorp/mmkv-kotlin.git")
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleTest/kotlin/com/ctrip/flight/mmkv/MMKVKMMTestIos.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import kotlinx.cinterop.BetaInteropApi
20 | import kotlinx.cinterop.ExperimentalForeignApi
21 | import kotlinx.cinterop.toKString
22 | import platform.Foundation.*
23 | import platform.posix.getenv
24 | import kotlin.experimental.ExperimentalNativeApi
25 | import kotlin.test.*
26 |
27 | /**
28 | * iOS unit test
29 | * @author yaqiao
30 | */
31 |
32 | class MMKVKotlinTestIos {
33 |
34 | private lateinit var mmkvTest: MMKVKotlinTest
35 |
36 | @BeforeTest
37 | @OptIn(ExperimentalForeignApi::class)
38 | fun setUp() {
39 | initialize("/Users/${getenv("USER")!!.toKString()}/Downloads")
40 | mmkvTest = MMKVKotlinTest().apply {
41 | setUp()
42 | }
43 | }
44 |
45 | @AfterTest
46 | fun setDown() {
47 | mmkvTest.testDown()
48 | }
49 |
50 | @Test
51 | fun testCommon() = with(mmkvTest) {
52 | testBoolean()
53 | testInt()
54 | testLong()
55 | testFloat()
56 | testDouble()
57 | testString()
58 | testByteArray()
59 | testUInt()
60 | testULong()
61 | testStringSet()
62 | testRemove()
63 | }
64 |
65 | @Test
66 | fun testNSDate() {
67 | val mmkvImpl = mmkvTest.mmkv as? MMKVImpl ?: throw IllegalStateException("MMKV type has some problems")
68 | val date = NSDate()
69 | val result = mmkvImpl.set("NSDate", date)
70 | assertEquals(result, true)
71 |
72 | val value0 = mmkvImpl.getNSDate("NSDate")
73 | assertEquals(value0?.timeIntervalSince1970, date.timeIntervalSince1970)
74 |
75 | val value1 = mmkvImpl.getNSDate(MMKVKotlinTest.KEY_NOT_EXIST)
76 | assertEquals(value1, null)
77 | }
78 |
79 | @Test
80 | @Suppress("UNCHECKED_CAST")
81 | @OptIn(BetaInteropApi::class)
82 | fun testNSObject() {
83 | val mmkvImpl = mmkvTest.mmkv as? MMKVImpl ?: throw IllegalStateException("MMKV type has some problems")
84 | val list = listOf("Aa", "Bb", "Cc")
85 |
86 | val result = mmkvImpl.set("NSObject", list as? NSArray)
87 | assertEquals(result, true)
88 |
89 | val value0 = mmkvImpl.getObject("NSObject", NSArray.`class`()!!)
90 | assertEquals(value0 as? List, list)
91 |
92 | val value1 = mmkvImpl.getObject(MMKVKotlinTest.KEY_NOT_EXIST, NSArray.`class`()!!)
93 | assertEquals(value1, null)
94 | }
95 |
96 | @Test
97 | @OptIn(ExperimentalNativeApi::class)
98 | fun emptyNsDataToByteArray() {
99 | @Suppress("CAST_NEVER_SUCCEEDS")
100 | val data = ("" as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData
101 | val bytes = data.toByteArray()
102 | assert(bytes.isEmpty())
103 | }
104 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # MMKV-Kotlin Change Log
2 |
3 | - Date format: YYYY-MM-dd
4 |
5 | ## v1.3.1 / 2025-10-25
6 |
7 | * Based on `Kotlin 2.2.21`, `MMKV 2.2.4`
8 |
9 | ## v1.3.0 / 2025-09-05
10 |
11 | * Based on `Kotlin 2.2.10`, `MMKV 2.2.3`
12 | * Deprecated all `takeXxx` functions, renamed to `getXxx` to avoid confusion ([issue#29](https://github.com/ctripcorp/mmkv-kotlin/issues/29))
13 |
14 | ## v1.2.18/ 2025-07-07
15 |
16 | * Based on `Kotlin 2.2.0`, `MMKV 2.2.2`
17 | * Fixed a bug of function `initilize()` ([issue#44](https://github.com/ctripcorp/mmkv-kotlin/issues/44))
18 | * All overload versions of function `initialize` could pass `null` as their parameter `rootDir`
19 |
20 | ## v1.2.17/ 2025-04-23
21 |
22 | * Based on `Kotlin 2.1.20`, `MMKV 2.1.0`
23 |
24 | ## v1.2.16/ 2025-02-06
25 |
26 | * Based on `Kotlin 2.1.10`, `MMKV 2.0.2`
27 |
28 | ## v1.2.15/ 2024-12-06
29 |
30 | * Based on `Kotlin 2.1.0`, `MMKV 2.0.0`
31 |
32 | ## v1.2.14/ 2024-08-31
33 |
34 | * Fixed the function `MMKV_KMP.withOpen`
35 | * Based on `Kotlin 2.0.20`, `MMKV 1.3.9`
36 |
37 | ## v1.2.13 / 2024-07-16
38 |
39 | * Based on `Kotlin 1.9.24`, `MMKV 1.3.7`
40 |
41 | ## v1.2.12 / 2024-04-30
42 |
43 | * Based on `Kotlin 1.9.23`, `MMKV 1.3.5`
44 |
45 | ## v1.2.11 / 2024-01-08
46 |
47 | * Update Kotlin version to `1.9.22`
48 |
49 | ## v1.2.10 / 2023-11-28
50 |
51 | * Update Kotlin version to `1.9.21` for fixing this [issue](https://youtrack.jetbrains.com/issue/KT-62515)
52 |
53 | ## v1.2.9 / 2023-11-24
54 |
55 | * Based on `Kotlin 1.9.20`, `MMKV 1.3.2`
56 |
57 | ## v1.2.8 / 2023-08-28
58 |
59 | * Based on `Kotlin 1.9.10`, `MMKV 1.3.1`
60 |
61 | ## v1.2.7 / 2023-06-09
62 |
63 | * Based on `Kotlin 1.8.20`, `MMKV 1.2.16`
64 |
65 | ## v1.2.6 / 2023-03-10
66 |
67 | * Based on `Kotlin 1.8.10`
68 |
69 | * Android Gradle Plugin: 7.4.0 -> 7.3.1 (temporary)
70 | * Android Build Tools: 33.0.1 -> 33.0.2
71 |
72 | ## v1.2.5 / 2023-01-23
73 |
74 | ### Update MMKV Version
75 |
76 | * Based on `MMKV 1.2.15`
77 |
78 | ### Update Other Build Tools
79 |
80 | * Android Gradle Plugin: 7.3.0 -> 7.4.0
81 |
82 | * Android Build Tools: 33.0.0 -> 33.0.1
83 |
84 | ## v1.2.5-alpha01 / 2022-12-28
85 |
86 | * Test Github Action Publishing
87 |
88 | ## v1.2.4 / 2022-10-14
89 |
90 | ### Update Kotlin Version
91 |
92 | * Based on `Kotlin 1.7.20`
93 |
94 | ### Update Other Build Tools
95 |
96 | * Gradle: 7.4.2 -> 7.5.1
97 |
98 | * Android Gradle Plugin: 7.2.1 -> 7.3.0
99 |
100 | * *com.gradle.enterprise*: 3.7 -> 3.11.2
101 |
102 | * Android Target SDK API: 32 -> 33
103 |
104 | * Android Build Tools: 32.0.0 -> 33.0.0
105 |
106 | ### Bug Fix
107 |
108 | * Fix the bug about empty `NSData` convert to `ByteArray` ([PR#2](https://github.com/ctripcorp/mmkv-kotlin/pull/2))
109 |
110 | ## v1.2.2 / 2022-08-25
111 |
112 | ### Update MMKV Version
113 |
114 | * Based on `MMKV 1.2.14`
115 |
116 | ## v1.2.1 / 2022-07-17
117 |
118 | ### Update Kotlin Version
119 |
120 | * Based on `Kotlin 1.7.10`
121 |
122 | ### Update Other Build Tools
123 |
124 | * Update Android Target SDK to API 32
125 |
126 | ## v1.2.0 / 2022-06-13
127 |
128 | ### Update Kotlin Version
129 |
130 | * Based on `Kotlin 1.7.0`
131 |
132 | ## v1.1.1 / 2022-05-05
133 |
134 | ### macOS Support
135 |
136 | * Add the macOS(x64/arm64) supported
137 |
138 | ## v1.1.0 / 2022-04-25
139 |
140 | ### Update Kotlin and MMKV Version
141 |
142 | * Based on `Kotlin 1.6.21`, `MMKV 1.2.13`
143 |
144 | ### Name Fix
145 |
146 | * Reamed the `MMKVModel` to `MMKVMode`
147 |
148 | ## v1.0.0 / 2022-04-19
149 |
150 | ### Initial Release
151 |
152 | * Based on `Kotlin 1.6.10`, `MMKV 1.2.12`
153 |
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidInstrumentedTest/kotlin/com/ctrip/flight/mmkv/MMKVKMMTestAndroid.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import android.os.Parcelable
22 | import android.os.SystemClock
23 | import androidx.test.core.app.ApplicationProvider
24 | import androidx.test.filters.SmallTest
25 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
26 | import androidx.test.platform.app.InstrumentationRegistry
27 | import kotlinx.parcelize.Parcelize
28 | import org.junit.After
29 | import org.junit.Before
30 | import org.junit.Test
31 | import org.junit.runner.RunWith
32 | import kotlin.test.assertEquals
33 |
34 | /**
35 | * Android unit test
36 | * @author yaqiao
37 | */
38 |
39 | @RunWith(AndroidJUnit4ClassRunner::class)
40 | @SmallTest
41 | class MMKVKotlinTestAndroid {
42 |
43 | private lateinit var mmkvTest: MMKVKotlinTest
44 |
45 | @Before
46 | fun setUp() {
47 | val context = ApplicationProvider.getApplicationContext()
48 | initialize(context)
49 | mmkvTest = MMKVKotlinTest().apply {
50 | setUp()
51 | testDown()
52 | }
53 | }
54 |
55 | @After
56 | fun setDown() {
57 | mmkvTest.testDown()
58 | }
59 |
60 | @Test
61 | fun testCommon() = with(mmkvTest) {
62 | testBoolean()
63 | testInt()
64 | testLong()
65 | testFloat()
66 | testDouble()
67 | testString()
68 | testByteArray()
69 | testUInt()
70 | testStringSet()
71 | testULong()
72 | }
73 |
74 | @Parcelize
75 | private data class TestParcelable(
76 | val id: Int,
77 | val name: String,
78 | val height: Float
79 | ) : Parcelable
80 |
81 | @Test
82 | fun testParcelable() = with(mmkvTest) {
83 | val androidMMKV = mmkv as? MMKVImpl ?: throw IllegalStateException("MMKV type has some problems")
84 | val testParcelable0 = TestParcelable(
85 | id = 1,
86 | name = "Tom",
87 | height = 176.5f,
88 | )
89 |
90 | val result = androidMMKV.set("Parcelable", testParcelable0)
91 | assertEquals(true, result)
92 |
93 | val value0 = androidMMKV.getParcelable("Parcelable", null, TestParcelable::class)
94 | assertEquals(testParcelable0, value0)
95 |
96 | val value1 = androidMMKV.getParcelable(MMKVKotlinTest.KEY_NOT_EXIST, null, TestParcelable::class)
97 | assertEquals(null, value1)
98 |
99 | val testParcelable1 = TestParcelable(
100 | id = 2,
101 | name = "Jerry",
102 | height = 180.0f,
103 | )
104 | val value2 = androidMMKV.getParcelable(MMKVKotlinTest.KEY_NOT_EXIST, testParcelable1, TestParcelable::class)
105 | assertEquals(testParcelable1, value2)
106 | }
107 |
108 | // @Test
109 | fun testIPCUpdateInt() {
110 | val mmkv = mmkvWithID(MMKVTestService.SharedMMKVID, MMKVMode.MULTI_PROCESS)
111 | mmkv[MMKVTestService.SharedMMKVKey] = 1024
112 | val context = InstrumentationRegistry.getInstrumentation().targetContext
113 | val intent = Intent(context, MMKVTestService::class.java)
114 | intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Update)
115 | context.startService(intent)
116 |
117 | SystemClock.sleep(1000 * 3)
118 | val intValue = mmkv.getInt(MMKVTestService.SharedMMKVKey)
119 | assertEquals(1024 + 1, intValue)
120 | }
121 |
122 | // @Test
123 | fun testIPCLock() {
124 | val context = InstrumentationRegistry.getInstrumentation().targetContext
125 | val intent = Intent(context, MMKVTestService::class.java)
126 | intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Lock)
127 | context.startService(intent)
128 |
129 | SystemClock.sleep(1000 * 3)
130 | val mmkv = mmkvWithID(MMKVTestService.SharedMMKVID, MMKVMode.MULTI_PROCESS) as? MMKVImpl ?: throw IllegalStateException("MMKV type has some problems")
131 | val result0 = mmkv.tryLock()
132 | assertEquals(false, result0)
133 |
134 | intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Kill)
135 | context.startService(intent)
136 | SystemClock.sleep(1000 * 3)
137 | val result1 = mmkv.tryLock()
138 | assertEquals(true, result1)
139 | }
140 | }
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # MMKV for Kotlin Multiplatform
2 |
3 | MMKV-Kotlin 是对 [MMKV](https://github.com/Tencent/MMKV) 到 Kotlin Multiplatform 的移植。当前支持 Android/iOS/macOS。
4 |
5 | ## 使用指南
6 |
7 | ### 在 Gradle 中使用 Maven 安装引入
8 |
9 | **Kotlin Multiplatform Common (kts):**
10 |
11 | ```kotlin
12 | dependencies {
13 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin:1.3.0")
14 | }
15 | ```
16 |
17 | 当前版本依赖于 `Kotlin 2.2.21` 以及 `MMKV 2.2.4`。
18 |
19 | **支持 iOS/macOS 的 Kotlin Multiplatform:**
20 |
21 | 如果你的 Kotlin Multiplatform 支持 iOS 或者 macOS,并且它会被构建为一个 Apple framework 供一个 Xcode 工程消费。那么你需要在你的 Xcode
22 | 工程中安装 [MMKV](https://github.com/Tencent/MMKV),请参考[这里](https://github.com/Tencent/MMKV/wiki/iOS_setup_cn)。
23 |
24 | **纯 Android 平台(kts):**
25 |
26 | ```kotlin
27 | dependencies {
28 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-android:1.3.0")
29 | }
30 | ```
31 |
32 | **Kotlin/Native on macOS:**
33 |
34 | ```kotlin
35 | dependencies {
36 | // Intel 芯片
37 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosx64:1.3.0")
38 |
39 | // Apple Silicon
40 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosarm64:1.3.0")
41 | }
42 | ```
43 | 注意,如果你的工程为 macOS 的 Kotlin/Native 可执行程序工程,或者它直接向一个 iOS 应用程序工程提供 framework,那么您需要手动在工程中添加对 MMKV 的依赖,并可能需要添加对 MMKV 及 MMKVCore 的 `linkerOpts`:
44 |
45 | ```kotlin
46 | kotlin {
47 | macosX64 {
48 | binaries {
49 | // ......
50 | all {
51 | val moduleName = "mmkv_operator"
52 | val mmkvPath = "${buildDir.absolutePath}/cocoapods/synthetic/OSX/$moduleName/build/Release/MMKV"
53 | val mmkvCorePath = "${buildDir.absolutePath}/cocoapods/synthetic/OSX/$moduleName//build/Release/MMKVCore"
54 | linkerOpts += listOf(
55 | "-F$mmkvPath",
56 | "-rpath", mmkvPath,
57 | "-framework", "MMKV",
58 | "-F$mmkvCorePath",
59 | "-rpath", mmkvCorePath,
60 | "-framework", "MMKVCore"
61 | )
62 | }
63 | }
64 | }
65 | cocoapods {
66 | // ......
67 | pod(name = "MMKV") {
68 | version = "2.2.4"
69 | moduleName = "MMKV"
70 | }
71 | }
72 | // ......
73 | }
74 | ```
75 |
76 | ### 初始化与配置根目录
77 |
78 | 可在 App 或进程启动时初始化 MMKV。由于 [MMKV-Android](https://github.com/Tencent/MMKV/tree/master/Android/MMKV) 的初始化 API 依赖 [Context](https://developer.android.com/reference/android/content/Context),因此 Android 与 iOS 平台初始化 API 有所不同。
79 |
80 | Android:
81 |
82 | ```kotlin
83 | import com.ctrip.flight.mmkv.initialize
84 |
85 | // In Android source set
86 | fun initializeMMKV(context: Context) {
87 | val rootDir = initialize(context)
88 | Log.d("MMKV Path", rootDir)
89 | }
90 | ```
91 |
92 | iOS:
93 |
94 | ```kotlin
95 | import com.ctrip.flight.mmkv.initialize
96 |
97 | // In iOS source set
98 | fun initializeMMKV(rootDir: String) {
99 | initialize(rootDir)
100 | println("MMKV Path: $rootDir")
101 | }
102 | ```
103 |
104 | 当然,您也可以在您的 Android 或 iOS app 工程中调用 [MMKV-Android](https://github.com/Tencent/MMKV/tree/master/Android/MMKV)(Java)或 [MMKV-iOS](https://github.com/Tencent/MMKV/tree/master/iOS)(Objective-C)的初始化 API 完成初始化。
105 |
106 | ### CRUD 操作
107 |
108 | - MMKV 提供一个**全局的实例**,可以直接使用:
109 |
110 | ```kotlin
111 | import com.ctrip.flight.mmkv.defaultMMKV
112 |
113 | fun demo() {
114 | val kv = defaultMMKV()
115 |
116 | kv.set("Boolean", true)
117 | println("Boolean: ${kv.getBoolean("Boolean")}")
118 |
119 | kv.set("Int", Int.MIN_VALUE)
120 | println("Int: ${kv.getInt("Int")}")
121 |
122 | kv.set("Long", Long.MAX_VALUE)
123 | println("Long: ${kv.getLong("Long")}")
124 |
125 | kv.set("Float", -3.14f)
126 | println("Float: ${kv.getFloat("Float")}")
127 |
128 | kv.set("Double", Double.MIN_VALUE)
129 | println("Double: ${kv.getDouble("Double")}")
130 |
131 | kv.set("String", "Hello from mmkv")
132 | println("String: ${kv.getString("String")}")
133 |
134 | val bytes = byteArrayOf(
135 | 'm'.code.toByte(),
136 | 'm'.code.toByte(),
137 | 'k'.code.toByte(),
138 | 'v'.code.toByte(),
139 | )
140 | kv.set("ByteArray", bytes)
141 | println("ByteArray: ${kv.getByteArray("ByteArray")?.toString()}")
142 | }
143 | ```
144 |
145 | - **删除 & 判断键是否存在:**
146 |
147 | ```kotlin
148 | kv.removeValueForKey("Boolean")
149 | println("Boolean: ${kv.getBoolean("Boolean")}")
150 |
151 | kv.removeValuesForKeys(listOf("Int", "Long"))
152 | println("allKeys: ${kv.allKeys()}")
153 |
154 | val hasBoolean = kv.containsKey("Boolean")
155 | ```
156 |
157 | - 如果不同业务需要**区别存储**,也可以单独创建自己的实例:
158 |
159 | ```kotlin
160 | import com.ctrip.flight.mmkv.mmkvWithID
161 | ...
162 | val kvWithMyId = mmkvWithID("MyID")
163 | kvWithMyId.set("Boolean", true)
164 | ```
165 |
166 | - 如果业务需要**多进程访问**,那么在初始化的时候加上标志位 `MMKVModel.MULTI_PROCESS`:
167 |
168 | ```kotlin
169 | import com.ctrip.flight.mmkv.mmkvWithID
170 | import com.ctrip.flight.mmkv.MMKVModel
171 |
172 | ...
173 | val kvMultiProcess = mmkvWithID("InterProcessKV", MMKVModel.MULTI_PROCESS)
174 | kvMultiProcess.set("Boolean", true)
175 | ```
176 |
177 | ## 支持的数据类型
178 |
179 | * 在 Kotlin Multiplatform common source set 中支持以下 Kotlin 类型:
180 | * `Boolean、Int、Long、Float、Double、String、UInt、ULong、ByteArray、Set`
181 |
182 |
183 | * 在 Android source set 中额外支持以下类型:
184 | * `Parcelable 的实现者`
185 |
186 |
187 | * 在 Apple source set 中额外支持以下类型:
188 | * `NSDate 以及 NSCoding 协议的实现者`
189 |
190 |
191 | ## 注意
192 |
193 | - MMKV-Kotlin 暂时不支持从 SharedPreferences 与 NSUserDefaults 中迁移旧数据。
194 |
195 | ## 开源许可
196 |
197 | 本项目于 [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 协议下开源。
198 |
199 | 查看 [LICENSE](LICENSE.txt) 获取更多信息。
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonMain/kotlin/com/ctrip/flight/mmkv/MMKV_KMP.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | /**
20 | * MMKV common source set expect class
21 | * @author yaqiao
22 | */
23 |
24 | interface MMKV_KMP {
25 |
26 | /**
27 | * Write value
28 | */
29 |
30 | operator fun set(key: String, value: String): Boolean
31 |
32 | operator fun set(key: String, value: Boolean): Boolean
33 |
34 | operator fun set(key: String, value: Int): Boolean
35 |
36 | operator fun set(key: String, value: Long): Boolean
37 |
38 | operator fun set(key: String, value: Float): Boolean
39 |
40 | operator fun set(key: String, value: Double): Boolean
41 |
42 | operator fun set(key: String, value: ByteArray): Boolean
43 |
44 | operator fun set(key: String, value: UInt): Boolean
45 |
46 | operator fun set(key: String, value: ULong): Boolean
47 |
48 | operator fun set(key: String, value: Set?): Boolean
49 |
50 | /**
51 | * Read value
52 | */
53 | @Deprecated(
54 | message = "Renamed to 'getString' for clarity, as the 'take' prefix could be confusing.",
55 | replaceWith = ReplaceWith("getString(key, default)")
56 | )
57 | fun takeString(key: String, default: String = ""): String
58 |
59 | @Deprecated(
60 | message = "Renamed to 'getBoolean' for clarity, as the 'take' prefix could be confusing.",
61 | replaceWith = ReplaceWith("getBoolean(key, default)")
62 | )
63 | fun takeBoolean(key: String, default: Boolean = false): Boolean
64 |
65 | @Deprecated(
66 | message = "Renamed to 'getInt' for clarity, as the 'take' prefix could be confusing.",
67 | replaceWith = ReplaceWith("getInt(key, default)")
68 | )
69 | fun takeInt(key: String, default: Int = 0): Int
70 |
71 | @Deprecated(
72 | message = "Renamed to 'getLong' for clarity, as the 'take' prefix could be confusing.",
73 | replaceWith = ReplaceWith("getLong(key, default)")
74 | )
75 | fun takeLong(key: String, default: Long = 0): Long
76 |
77 | @Deprecated(
78 | message = "Renamed to 'getFloat' for clarity, as the 'take' prefix could be confusing.",
79 | replaceWith = ReplaceWith("getFloat(key, default)")
80 | )
81 | fun takeFloat(key: String, default: Float = 0f): Float
82 |
83 | @Deprecated(
84 | message = "Renamed to 'getDouble' for clarity, as the 'take' prefix could be confusing.",
85 | replaceWith = ReplaceWith("getDouble(key, default)")
86 | )
87 | fun takeDouble(key: String, default: Double = 0.0): Double
88 |
89 | @Deprecated(
90 | message = "Renamed to 'getByteArray' for clarity, as the 'take' prefix could be confusing.",
91 | replaceWith = ReplaceWith("getByteArray(key, default)")
92 | )
93 | fun takeByteArray(key: String, default: ByteArray? = null): ByteArray?
94 |
95 | @Deprecated(
96 | message = "Renamed to 'getUInt' for clarity, as the 'take' prefix could be confusing.",
97 | replaceWith = ReplaceWith("getUInt(key, default)")
98 | )
99 | fun takeUInt(key: String, default: UInt = 0u): UInt
100 |
101 | @Deprecated(
102 | message = "Renamed to 'getULong' for clarity, as the 'take' prefix could be confusing.",
103 | replaceWith = ReplaceWith("getULong(key, default)")
104 | )
105 | fun takeULong(key: String, default: ULong = 0u): ULong
106 |
107 | @Deprecated(
108 | message = "Renamed to 'getStringSet' for clarity, as the 'take' prefix could be confusing.",
109 | replaceWith = ReplaceWith("getStringSet(key, default)")
110 | )
111 | fun takeStringSet(key: String, default: Set? = null): Set?
112 |
113 | fun getString(key: String, default: String = ""): String
114 |
115 | fun getBoolean(key: String, default: Boolean = false): Boolean
116 |
117 | fun getInt(key: String, default: Int = 0): Int
118 |
119 | fun getLong(key: String, default: Long = 0): Long
120 |
121 | fun getFloat(key: String, default: Float = 0f): Float
122 |
123 | fun getDouble(key: String, default: Double = 0.0): Double
124 |
125 | fun getByteArray(key: String, default: ByteArray? = null): ByteArray?
126 |
127 | fun getUInt(key: String, default: UInt = 0u): UInt
128 |
129 | fun getULong(key: String, default: ULong = 0u): ULong
130 |
131 | fun getStringSet(key: String, default: Set? = null): Set?
132 |
133 | /**
134 | * Remove value
135 | */
136 |
137 | fun removeValueForKey(key: String)
138 |
139 | fun removeValuesForKeys(keys: List)
140 |
141 | /**
142 | * Size
143 | */
144 |
145 | val actualSize: Long
146 |
147 | val count: Long
148 |
149 | val totalSize: Long
150 |
151 | /**
152 | * Clear
153 | */
154 |
155 | fun clearMemoryCache()
156 |
157 | fun clearAll()
158 |
159 | /**
160 | * Other
161 | */
162 |
163 | fun close()
164 |
165 | fun allKeys(): List
166 |
167 | fun containsKey(key: String): Boolean
168 |
169 | fun checkReSetCryptKey(key: String?)
170 |
171 | fun mmapID(): String
172 |
173 | fun async()
174 |
175 | fun sync()
176 |
177 | fun trim()
178 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MMKV for Kotlin Multiplatform
2 |
3 | 中文版本请参看[这里](README_CN.md)
4 |
5 | MMKV-Kotlin is a porting of [MMKV](https://github.com/Tencent/MMKV) to Kotlin Multiplatform. Currently, Android/iOS/macOS are supported.
6 |
7 | ## Tutorial
8 |
9 | ### Installation Via Maven in Gradle
10 |
11 | **Kotlin Multiplatform Common (kts):**
12 |
13 | ```kotlin
14 | dependencies {
15 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin:1.3.0")
16 | }
17 | ```
18 |
19 | Current version is based on `Kotlin 2.2.21` and `MMKV 2.2.4`.
20 |
21 | **Kotlin Multiplatform for iOS/macOS applications:**
22 |
23 | If your Kotlin Multiplatform project supports iOS or macOS, and it would be built to an Apple framework that will be
24 | consumed by an Xcode project. You need to install [MMKV](https://github.com/Tencent/MMKV) into your Xcode project, please refer [that](https://github.com/Tencent/MMKV/wiki/iOS_setup).
25 |
26 | **Pure Android platform (kts):**
27 |
28 | ```kotlin
29 | dependencies {
30 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-android:1.3.0")
31 | }
32 | ```
33 |
34 | **Kotlin/Native on macOS:**
35 |
36 | ```kotlin
37 | dependencies {
38 | // Intel Chip
39 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosx64:1.3.0")
40 |
41 | // Apple Silicon
42 | implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosarm64:1.3.0")
43 | }
44 | ```
45 | Note, if your project is a Kotlin/Native executable program project of macOS, or it supplies a framework to an iOS application project directly, then you need to manually add the dependency of MMKV, and may need to add `linkerOpts` for MMKV and MMKVCore:
46 |
47 | ```kotlin
48 | kotlin {
49 | macosX64 {
50 | binaries {
51 | // ......
52 | all {
53 | val moduleName = "mmkv_operator"
54 | val mmkvPath = "${buildDir.absolutePath}/cocoapods/synthetic/OSX/$moduleName/build/Release/MMKV"
55 | val mmkvCorePath = "${buildDir.absolutePath}/cocoapods/synthetic/OSX/$moduleName//build/Release/MMKVCore"
56 | linkerOpts += listOf(
57 | "-F$mmkvPath",
58 | "-rpath", mmkvPath,
59 | "-framework", "MMKV",
60 | "-F$mmkvCorePath",
61 | "-rpath", mmkvCorePath,
62 | "-framework", "MMKVCore"
63 | )
64 | }
65 | }
66 | }
67 | cocoapods {
68 | // ......
69 | pod(name = "MMKV") {
70 | version = "2.2.4"
71 | moduleName = "MMKV"
72 | }
73 | }
74 | // ......
75 | }
76 | ```
77 |
78 | ### Initialization and Configure Root Path
79 |
80 | You can initialize MMKV when your app or the process initialization. [MMKV-Android](https://github.com/Tencent/MMKV/tree/master/Android/MMKV) initialization API depends on [Context](https://developer.android.com/reference/android/content/Context). So, Android's initialization API is different with iOS.
81 |
82 | Android:
83 |
84 | ```kotlin
85 | import com.ctrip.flight.mmkv.initialize
86 |
87 | // In Android source set
88 | fun initializeMMKV(context: Context) {
89 | val rootDir = initialize(context)
90 | Log.d("MMKV Path", rootDir)
91 | }
92 | ```
93 |
94 | iOS:
95 |
96 | ```kotlin
97 | import com.ctrip.flight.mmkv.initialize
98 |
99 | // In iOS source set
100 | fun initializeMMKV(rootDir: String) {
101 | initialize(rootDir)
102 | println("MMKV Path: $rootDir")
103 | }
104 | ```
105 |
106 | You also could call [MMKV-Android](https://github.com/Tencent/MMKV/tree/master/Android/MMKV) (Java) or [MMKV-iOS](https://github.com/Tencent/MMKV/tree/master/iOS) (Objective-C) initialization API in your Android or iOS app project.
107 |
108 | ### CRUD Operations
109 |
110 | - MMKV has a **global instance**, you can use it directly:
111 |
112 | ```kotlin
113 | import com.ctrip.flight.mmkv.defaultMMKV
114 |
115 | fun demo() {
116 | val kv = defaultMMKV()
117 |
118 | kv.set("Boolean", true)
119 | println("Boolean: ${kv.getBoolean("Boolean")}")
120 |
121 | kv.set("Int", Int.MIN_VALUE)
122 | println("Int: ${kv.getInt("Int")}")
123 |
124 | kv.set("Long", Long.MAX_VALUE)
125 | println("Long: ${kv.getLong("Long")}")
126 |
127 | kv.set("Float", -3.14f)
128 | println("Float: ${kv.getFloat("Float")}")
129 |
130 | kv.set("Double", Double.MIN_VALUE)
131 | println("Double: ${kv.getDouble("Double")}")
132 |
133 | kv.set("String", "Hello from mmkv")
134 | println("String: ${kv.getString("String")}")
135 |
136 | val bytes = byteArrayOf(
137 | 'm'.code.toByte(),
138 | 'm'.code.toByte(),
139 | 'k'.code.toByte(),
140 | 'v'.code.toByte(),
141 | )
142 | kv.set("ByteArray", bytes)
143 | println("ByteArray: ${kv.getByteArray("ByteArray")?.toString()}")
144 | }
145 | ```
146 |
147 | - **Deleting & Judging (Whether the key is existed):**
148 |
149 | ```kotlin
150 | kv.removeValueForKey("Boolean")
151 | println("Boolean: ${kv.getBoolean("Boolean")}")
152 |
153 | kv.removeValuesForKeys(listOf("Int", "Long"))
154 | println("allKeys: ${kv.allKeys()}")
155 |
156 | val hasBoolean = kv.containsKey("Boolean")
157 | ```
158 |
159 | - If a different modules/logics need **isolated storage**, you can also create your own MMKV instance separately:
160 |
161 | ```kotlin
162 | import com.ctrip.flight.mmkv.mmkvWithID
163 | ...
164 | val kvWithMyId = mmkvWithID("MyID")
165 | kvWithMyId.set("Boolean", true)
166 | ```
167 |
168 | - If **multi-process accessing** is needed, you can set `MMKVMode.MULTI_PROCESS` when MMKV initialization:
169 |
170 | ```kotlin
171 | import com.ctrip.flight.mmkv.mmkvWithID
172 | import com.ctrip.flight.mmkv.MMKVModel
173 |
174 | ...
175 | val kvMultiProcess = mmkvWithID("InterProcessKV", MMKVModel.MULTI_PROCESS)
176 | kvMultiProcess.set("Boolean", true)
177 | ```
178 |
179 | ## Supported Types
180 |
181 | * Supported Kotlin types in Kotlin Multiplatform common source set:
182 | * `Boolean, Int, Long, Float, Double, String, UInt, ULong, ByteArray, Set`
183 |
184 |
185 | * The following types are additionally supported in the Android source set:
186 | * `Any class that implements Parcelable`
187 |
188 |
189 | * The following types are additionally supported in the Apple source set:
190 | * `NSDate and any class that implements NSCoding protocol`
191 |
192 |
193 | ## Note
194 |
195 | - MMKV-Kotlin currently does not support migrating old data from SharedPreferences and NSUserDefaults.
196 |
197 | ## License
198 |
199 | Distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
200 |
201 | See [LICENSE](LICENSE.txt) for more information.
--------------------------------------------------------------------------------
/mmkv-kotlin/src/commonTest/kotlin/com/ctrip/flight/mmkv/MMKVKMMTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import kotlin.test.assertContentEquals
20 | import kotlin.test.assertEquals
21 |
22 | /**
23 | * MMKV_Kotlin core unit test
24 | * @author yaqiao
25 | */
26 |
27 | class MMKVKotlinTest {
28 |
29 | companion object {
30 | const val KEY_NOT_EXIST = "Key_Not_Exist"
31 | }
32 |
33 | lateinit var mmkv: MMKV_KMP
34 | private set
35 |
36 | fun setUp() {
37 | mmkv = mmkvWithID("unitTest", cryptKey = "UnitTestCryptKey")
38 | }
39 |
40 | fun testDown() {
41 | mmkv.clearAll()
42 | }
43 |
44 | fun testBoolean() {
45 | val result = mmkv.set("Boolean", true)
46 | assertEquals(true, result)
47 | val value0 = mmkv.getBoolean("Boolean")
48 | assertEquals(true, value0)
49 | val value1 = mmkv.getBoolean(KEY_NOT_EXIST)
50 | assertEquals(false, value1)
51 | val value2 = mmkv.getBoolean(KEY_NOT_EXIST, true)
52 | assertEquals(true, value2)
53 | }
54 |
55 | fun testInt() {
56 | val result = mmkv.set("Int", Int.MAX_VALUE)
57 | assertEquals(true, result)
58 | val value0 = mmkv.getInt("Int")
59 | assertEquals(Int.MAX_VALUE, value0)
60 | val value1 = mmkv.getInt(KEY_NOT_EXIST)
61 | assertEquals(0, value1)
62 | val value2 = mmkv.getInt(KEY_NOT_EXIST, -1)
63 | assertEquals(-1, value2)
64 | }
65 |
66 | fun testLong() {
67 | val result = mmkv.set("Long", Long.MAX_VALUE)
68 | assertEquals(true, result)
69 | val value0 = mmkv.getLong("Long")
70 | assertEquals(Long.MAX_VALUE, value0)
71 | val value1 = mmkv.getLong(KEY_NOT_EXIST)
72 | assertEquals(0, value1)
73 | val value2 = mmkv.getLong(KEY_NOT_EXIST, -1)
74 | assertEquals(-1, value2)
75 | }
76 |
77 | fun testFloat() {
78 | val result = mmkv.set("Float", Float.MAX_VALUE)
79 | assertEquals(true, result)
80 | val value0 = mmkv.getFloat("Float")
81 | assertEquals(Float.MAX_VALUE, value0)
82 | val value1 = mmkv.getFloat(KEY_NOT_EXIST)
83 | assertEquals(0f, value1)
84 | val value2 = mmkv.getFloat(KEY_NOT_EXIST, -1f)
85 | assertEquals(-1f, value2)
86 | }
87 |
88 | fun testDouble() {
89 | val result = mmkv.set("Double", Double.MAX_VALUE)
90 | assertEquals(true, result)
91 | val value0 = mmkv.getDouble("Double")
92 | assertEquals(Double.MAX_VALUE, value0)
93 | val value1 = mmkv.getDouble(KEY_NOT_EXIST)
94 | assertEquals(0.0, value1)
95 | val value2 = mmkv.getDouble(KEY_NOT_EXIST, -1.0)
96 | assertEquals(-1.0, value2)
97 | }
98 |
99 | fun testString() {
100 | val str = "Hello MMKV with Kotlin"
101 | val result = mmkv.set("String", str)
102 | assertEquals(true, result)
103 | val value0 = mmkv.getString("String")
104 | assertEquals(str, value0)
105 | val value1 = mmkv.getString(KEY_NOT_EXIST)
106 | assertEquals("", value1)
107 | val emptyStr = "Empty"
108 | val value2 = mmkv.getString(KEY_NOT_EXIST, emptyStr)
109 | assertEquals(emptyStr, value2)
110 | }
111 |
112 | fun testByteArray() {
113 | val byteArray = byteArrayOf(1, 0, 0, 8, 6)
114 | val result = mmkv.set("ByteArray", byteArray)
115 | assertEquals(true, result)
116 | val value0 = mmkv.getByteArray("ByteArray")
117 | assertContentEquals(byteArray, value0)
118 | val value1 = mmkv.getByteArray(KEY_NOT_EXIST)
119 | assertContentEquals(null, value1)
120 | }
121 |
122 | fun testUInt() {
123 | val result = mmkv.set("UInt", UInt.MAX_VALUE)
124 | assertEquals(true, result)
125 | val value0 = mmkv.getUInt("UInt")
126 | assertEquals(UInt.MAX_VALUE, value0)
127 | val value1 = mmkv.getUInt(KEY_NOT_EXIST)
128 | assertEquals(0U, value1)
129 | val value2 = mmkv.getUInt(KEY_NOT_EXIST, 99U)
130 | assertEquals(99U, value2)
131 | }
132 |
133 | fun testULong() {
134 | val result = mmkv.set("ULong", ULong.MAX_VALUE)
135 | assertEquals(true, result)
136 | val value0 = mmkv.getULong("ULong")
137 | assertEquals(ULong.MAX_VALUE, value0)
138 | val value1 = mmkv.getULong(KEY_NOT_EXIST)
139 | assertEquals(0UL, value1)
140 | val value2 = mmkv.getULong(KEY_NOT_EXIST, 999UL)
141 | assertEquals(999UL, value2)
142 | }
143 |
144 | fun testRemove() {
145 | val byteArray = byteArrayOf(2, 0, 0 ,8, 8, 8)
146 | with(mmkv) {
147 | var result = set("Boolean_1", true)
148 | result = result && set("Int_1", Int.MAX_VALUE)
149 | result = result && set("Long_1", Long.MAX_VALUE)
150 | result = result && set("Float_1", Float.MAX_VALUE)
151 | result = result && set("Double_1", Double.MAX_VALUE)
152 | result = result && set("String_1", "hello")
153 | result = result && set("UInt_1", UInt.MAX_VALUE)
154 | result = result && set("ULong_1", ULong.MAX_VALUE)
155 | result = result && set("ByteArray_1", byteArray)
156 | assertEquals(true, result)
157 | }
158 |
159 | val oldCount = mmkv.count
160 | mmkv.removeValueForKey("Boolean_1")
161 | mmkv.removeValuesForKeys(listOf("Int_1", "Long_1"))
162 | assertEquals(mmkv.count, oldCount - 3)
163 |
164 | val resultB = mmkv.getBoolean("Boolean_1", false)
165 | assertEquals(false, resultB)
166 |
167 | val resultI = mmkv.getInt("Int_1")
168 | assertEquals(0, resultI)
169 |
170 | val resultL = mmkv.getLong("Long_1")
171 | assertEquals(0L, resultL)
172 |
173 | val resultF = mmkv.getFloat("Float_1")
174 | assertEquals(Float.MAX_VALUE, resultF)
175 |
176 | val resultD = mmkv.getDouble("Double_1")
177 | assertEquals(Double.MAX_VALUE, resultD)
178 |
179 | val resultS = mmkv.getString("String_1")
180 | assertEquals("hello", resultS)
181 |
182 | val resultUI = mmkv.getUInt("UInt_1")
183 | assertEquals(UInt.MAX_VALUE, resultUI)
184 |
185 | val resultUL = mmkv.getULong("ULong_1")
186 | assertEquals(ULong.MAX_VALUE, resultUL)
187 |
188 | val resultBA = mmkv.getByteArray("ByteArray_1")
189 | assertContentEquals(byteArray, resultBA)
190 | }
191 |
192 | fun testStringSet() {
193 | val strSet0 = setOf("t", "r", "i", "p")
194 | val result = mmkv.set("StringSet", strSet0)
195 | assertEquals(true, result)
196 |
197 | val value0 = mmkv.getStringSet("StringSet")
198 | assertEquals(strSet0, value0)
199 |
200 | val value1 = mmkv.getStringSet(KEY_NOT_EXIST)
201 | assertEquals(null, value1)
202 |
203 | val value2 = mmkv.getStringSet(KEY_NOT_EXIST)
204 | assertEquals(null, value2)
205 | }
206 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/androidMain/kotlin/com/ctrip/flight/mmkv/MMKVImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import com.tencent.mmkv.MMKV
20 |
21 | /**
22 | * MMKV class Android actual
23 | * @author yaqiao
24 | */
25 |
26 | class MMKVImpl internal constructor(internal val platformMMKV: MMKV) : MMKV_KMP {
27 |
28 | /**
29 | * Write value
30 | */
31 |
32 | override operator fun set(key: String, value: String): Boolean = platformMMKV.encode(key, value)
33 |
34 | override operator fun set(key: String, value: Boolean): Boolean = platformMMKV.encode(key, value)
35 |
36 | override operator fun set(key: String, value: Int): Boolean = platformMMKV.encode(key, value)
37 |
38 | override operator fun set(key: String, value: Long): Boolean = platformMMKV.encode(key, value)
39 |
40 | override operator fun set(key: String, value: Float): Boolean = platformMMKV.encode(key, value)
41 |
42 | override operator fun set(key: String, value: Double): Boolean = platformMMKV.encode(key, value)
43 |
44 | override operator fun set(key: String, value: ByteArray): Boolean = platformMMKV.encode(key, value)
45 |
46 | override operator fun set(key: String, value: UInt): Boolean = platformMMKV.encode(key, value.toInt())
47 |
48 | override operator fun set(key: String, value: ULong): Boolean = platformMMKV.encode(key, value.toLong())
49 |
50 | override operator fun set(key: String, value: Set?): Boolean = platformMMKV.encode(key, value)
51 |
52 | /**
53 | * Read value
54 | */
55 |
56 | @Deprecated(
57 | message = "Renamed to 'getString' for clarity, as the 'take' prefix could be confusing.",
58 | replaceWith = ReplaceWith("getString(key, default)")
59 | )
60 | override fun takeString(key: String, default: String): String = platformMMKV.decodeString(key, default) ?: default
61 |
62 | @Deprecated(
63 | message = "Renamed to 'getBoolean' for clarity, as the 'take' prefix could be confusing.",
64 | replaceWith = ReplaceWith("getBoolean(key, default)")
65 | )
66 | override fun takeBoolean(key: String, default: Boolean): Boolean = platformMMKV.decodeBool(key, default)
67 |
68 | @Deprecated(
69 | message = "Renamed to 'getInt' for clarity, as the 'take' prefix could be confusing.",
70 | replaceWith = ReplaceWith("getInt(key, default)")
71 | )
72 | override fun takeInt(key: String, default: Int): Int = platformMMKV.decodeInt(key, default)
73 |
74 | @Deprecated(
75 | message = "Renamed to 'getLong' for clarity, as the 'take' prefix could be confusing.",
76 | replaceWith = ReplaceWith("getLong(key, default)")
77 | )
78 | override fun takeLong(key: String, default: Long): Long = platformMMKV.decodeLong(key, default)
79 |
80 | @Deprecated(
81 | message = "Renamed to 'getFloat' for clarity, as the 'take' prefix could be confusing.",
82 | replaceWith = ReplaceWith("getFloat(key, default)")
83 | )
84 | override fun takeFloat(key: String, default: Float): Float = platformMMKV.decodeFloat(key, default)
85 |
86 | @Deprecated(
87 | message = "Renamed to 'getDouble' for clarity, as the 'take' prefix could be confusing.",
88 | replaceWith = ReplaceWith("getDouble(key, default)")
89 | )
90 | override fun takeDouble(key: String, default: Double): Double = platformMMKV.decodeDouble(key, default)
91 |
92 | @Deprecated(
93 | message = "Renamed to 'getByteArray' for clarity, as the 'take' prefix could be confusing.",
94 | replaceWith = ReplaceWith("getByteArray(key, default)")
95 | )
96 | override fun takeByteArray(key: String, default: ByteArray?): ByteArray? = platformMMKV.decodeBytes(key, default)
97 |
98 | @Deprecated(
99 | message = "Renamed to 'getUInt' for clarity, as the 'take' prefix could be confusing.",
100 | replaceWith = ReplaceWith("getUInt(key, default)")
101 | )
102 | override fun takeUInt(key: String, default: UInt): UInt = platformMMKV.decodeInt(key, default.toInt()).toUInt()
103 |
104 | @Deprecated(
105 | message = "Renamed to 'getULong' for clarity, as the 'take' prefix could be confusing.",
106 | replaceWith = ReplaceWith("getULong(key, default)")
107 | )
108 | override fun takeULong(key: String, default: ULong): ULong = platformMMKV.decodeLong(key, default.toLong()).toULong()
109 |
110 | @Deprecated(
111 | message = "Renamed to 'getStringSet' for clarity, as the 'take' prefix could be confusing.",
112 | replaceWith = ReplaceWith("getStringSet(key, default)")
113 | )
114 | override fun takeStringSet(key: String, default: Set?): Set? = platformMMKV.decodeStringSet(key, default)
115 |
116 | override fun getString(key: String, default: String): String = platformMMKV.decodeString(key, default) ?: default
117 |
118 | override fun getBoolean(key: String, default: Boolean): Boolean = platformMMKV.getBoolean(key, default)
119 |
120 | override fun getInt(key: String, default: Int): Int = platformMMKV.decodeInt(key, default)
121 |
122 | override fun getLong(key: String, default: Long): Long = platformMMKV.decodeLong(key, default)
123 |
124 | override fun getFloat(key: String, default: Float): Float = platformMMKV.decodeFloat(key, default)
125 |
126 | override fun getDouble(key: String, default: Double): Double = platformMMKV.decodeDouble(key, default)
127 |
128 | override fun getByteArray(key: String, default: ByteArray?): ByteArray? = platformMMKV.decodeBytes(key, default)
129 |
130 | override fun getUInt(key: String, default: UInt): UInt = platformMMKV.decodeInt(key, default.toInt()).toUInt()
131 |
132 | override fun getULong(key: String, default: ULong): ULong = platformMMKV.decodeLong(key, default.toLong()).toULong()
133 |
134 | override fun getStringSet(key: String, default: Set?): Set? = platformMMKV.decodeStringSet(key, default)
135 |
136 | /**
137 | * Remove value
138 | */
139 |
140 | override fun removeValueForKey(key: String) = platformMMKV.removeValueForKey(key)
141 |
142 | override fun removeValuesForKeys(keys: List) = platformMMKV.removeValuesForKeys(keys.toTypedArray())
143 |
144 | /**
145 | * Size
146 | */
147 |
148 | override val actualSize: Long
149 | get() = platformMMKV.actualSize()
150 |
151 | override val count: Long
152 | get() = platformMMKV.count()
153 |
154 | override val totalSize: Long
155 | get() = platformMMKV.totalSize()
156 |
157 | /**
158 | * Clear
159 | */
160 |
161 | override fun clearMemoryCache() = platformMMKV.clearMemoryCache()
162 |
163 | override fun clearAll() = platformMMKV.clearAll()
164 |
165 | /**
166 | * Other
167 | */
168 |
169 | override fun close() = platformMMKV.close()
170 |
171 | override fun allKeys(): List = platformMMKV.allKeys()?.toList() ?: listOf()
172 |
173 | override fun containsKey(key: String): Boolean = platformMMKV.containsKey(key)
174 |
175 | override fun checkReSetCryptKey(key: String?) = platformMMKV.checkReSetCryptKey(key)
176 |
177 | override fun mmapID(): String = platformMMKV.mmapID()
178 |
179 | override fun async() = platformMMKV.async()
180 |
181 | override fun sync() = platformMMKV.sync()
182 |
183 | override fun trim() = platformMMKV.trim()
184 |
185 | fun lock() = platformMMKV.lock()
186 |
187 | fun unLock() = platformMMKV.unlock()
188 |
189 | fun tryLock(): Boolean = platformMMKV.tryLock()
190 | }
--------------------------------------------------------------------------------
/mmkv-kotlin/src/appleMain/kotlin/com/ctrip/flight/mmkv/MMKVImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 Ctrip.com.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.ctrip.flight.mmkv
18 |
19 | import cocoapods.MMKV.MMKV
20 | import kotlinx.cinterop.BetaInteropApi
21 | import kotlinx.cinterop.ExperimentalForeignApi
22 | import platform.Foundation.NSSet
23 |
24 | /**
25 | * MMKV class iOS actual
26 | * @author yaqiao
27 | */
28 |
29 | @Suppress("UNCHECKED_CAST")
30 | @OptIn(ExperimentalForeignApi::class)
31 | class MMKVImpl internal constructor(internal val platformMMKV: MMKV) : MMKV_KMP {
32 |
33 | /**
34 | * Write value
35 | */
36 |
37 | override operator fun set(key: String, value: Int): Boolean = platformMMKV.setInt32(value, key)
38 |
39 | override operator fun set(key: String, value: Boolean): Boolean = platformMMKV.setBool(value, key)
40 |
41 | override operator fun set(key: String, value: String): Boolean = platformMMKV.setString(value, key)
42 |
43 | override operator fun set(key: String, value: Long): Boolean = platformMMKV.setInt64(value, key)
44 |
45 | override operator fun set(key: String, value: Float): Boolean = platformMMKV.setFloat(value, key)
46 |
47 | override operator fun set(key: String, value: Double): Boolean = platformMMKV.setDouble(value, key)
48 |
49 | override operator fun set(key: String, value: ByteArray): Boolean = platformMMKV.setData(value.toNSData(), key)
50 |
51 | override operator fun set(key: String, value: UInt): Boolean = platformMMKV.setUInt32(value, key)
52 |
53 | override operator fun set(key: String, value: ULong): Boolean = platformMMKV.setUInt64(value, key)
54 |
55 | override operator fun set(key: String, value: Set?): Boolean = platformMMKV.setObject(value as? NSSet, key)
56 |
57 | /**
58 | * Read value
59 | */
60 |
61 | @Deprecated(
62 | message = "Renamed to 'getString' for clarity, as the 'take' prefix could be confusing.",
63 | replaceWith = ReplaceWith("getString(key, default)")
64 | )
65 | override fun takeString(key: String, default: String): String = platformMMKV.getStringForKey(key, default) ?: default
66 |
67 | @Deprecated(
68 | message = "Renamed to 'getBoolean' for clarity, as the 'take' prefix could be confusing.",
69 | replaceWith = ReplaceWith("getBoolean(key, default)")
70 | )
71 | override fun takeBoolean(key: String, default: Boolean): Boolean = platformMMKV.getBoolForKey(key, default)
72 |
73 | @Deprecated(
74 | message = "Renamed to 'getInt' for clarity, as the 'take' prefix could be confusing.",
75 | replaceWith = ReplaceWith("getInt(key, default)")
76 | )
77 | override fun takeInt(key: String, default: Int): Int = platformMMKV.getInt32ForKey(key, default)
78 |
79 | @Deprecated(
80 | message = "Renamed to 'getLong' for clarity, as the 'take' prefix could be confusing.",
81 | replaceWith = ReplaceWith("getLong(key, default)")
82 | )
83 | override fun takeLong(key: String, default: Long): Long = platformMMKV.getInt64ForKey(key, default)
84 |
85 | @Deprecated(
86 | message = "Renamed to 'getFloat' for clarity, as the 'take' prefix could be confusing.",
87 | replaceWith = ReplaceWith("getFloat(key, default)")
88 | )
89 | override fun takeFloat(key: String, default: Float): Float = platformMMKV.getFloatForKey(key, default)
90 |
91 | @Deprecated(
92 | message = "Renamed to 'getDouble' for clarity, as the 'take' prefix could be confusing.",
93 | replaceWith = ReplaceWith("getDouble(key, default)")
94 | )
95 | override fun takeDouble(key: String, default: Double): Double = platformMMKV.getDoubleForKey(key, default)
96 |
97 | @Deprecated(
98 | message = "Renamed to 'getByteArray' for clarity, as the 'take' prefix could be confusing.",
99 | replaceWith = ReplaceWith("getByteArray(key, default)")
100 | )
101 | override fun takeByteArray(key: String, default: ByteArray?): ByteArray? = platformMMKV.getDataForKey(key, default?.toNSData())?.toByteArray()
102 |
103 | @Deprecated(
104 | message = "Renamed to 'getUInt' for clarity, as the 'take' prefix could be confusing.",
105 | replaceWith = ReplaceWith("getUInt(key, default)")
106 | )
107 | override fun takeUInt(key: String, default: UInt): UInt = platformMMKV.getUInt32ForKey(key, default)
108 |
109 | @Deprecated(
110 | message = "Renamed to 'getULong' for clarity, as the 'take' prefix could be confusing.",
111 | replaceWith = ReplaceWith("getULong(key, default)")
112 | )
113 | override fun takeULong(key: String, default: ULong): ULong = platformMMKV.getUInt64ForKey(key, default)
114 |
115 | @OptIn(BetaInteropApi::class)
116 | @Deprecated(
117 | message = "Renamed to 'getStringSet' for clarity, as the 'take' prefix could be confusing.",
118 | replaceWith = ReplaceWith("getStringSet(key, default)")
119 | )
120 | override fun takeStringSet(key: String, default: Set?): Set? = platformMMKV.getObjectOfClass(NSSet.`class`()!!, key) as? Set ?: default
121 |
122 | override fun getString(key: String, default: String): String = platformMMKV.getStringForKey(key, default) ?: default
123 |
124 | override fun getBoolean(key: String, default: Boolean): Boolean = platformMMKV.getBoolForKey(key, default)
125 |
126 | override fun getInt(key: String, default: Int): Int = platformMMKV.getInt32ForKey(key, default)
127 |
128 | override fun getLong(key: String, default: Long): Long = platformMMKV.getInt64ForKey(key, default)
129 |
130 | override fun getFloat(key: String, default: Float): Float = platformMMKV.getFloatForKey(key, default)
131 |
132 | override fun getDouble(key: String, default: Double): Double = platformMMKV.getDoubleForKey(key, default)
133 |
134 | override fun getByteArray(key: String, default: ByteArray?): ByteArray? = platformMMKV.getDataForKey(key, default?.toNSData())?.toByteArray()
135 |
136 | override fun getUInt(key: String, default: UInt): UInt = platformMMKV.getUInt32ForKey(key, default)
137 |
138 | override fun getULong(key: String, default: ULong): ULong = platformMMKV.getUInt64ForKey(key, default)
139 |
140 | @OptIn(BetaInteropApi::class)
141 | override fun getStringSet(key: String, default: Set?): Set? = platformMMKV.getObjectOfClass(NSSet.`class`()!!, key) as? Set ?: default
142 |
143 | /**
144 | * Remove value
145 | */
146 |
147 | override fun removeValueForKey(key: String) = platformMMKV.removeValueForKey(key)
148 |
149 | override fun removeValuesForKeys(keys: List) = platformMMKV.removeValuesForKeys(keys)
150 |
151 | /**
152 | * Size
153 | */
154 |
155 | override val actualSize: Long
156 | get() = platformMMKV.actualSize().toLong()
157 |
158 | override val count: Long
159 | get() = platformMMKV.count().toLong()
160 |
161 | override val totalSize: Long
162 | get() = platformMMKV.totalSize().toLong()
163 |
164 | /**
165 | * Clear
166 | */
167 |
168 | override fun clearMemoryCache() = platformMMKV.clearMemoryCache()
169 |
170 | override fun clearAll() = platformMMKV.clearAll()
171 |
172 | /**
173 | * Other
174 | */
175 |
176 | override fun close() = platformMMKV.close()
177 |
178 | override fun allKeys(): List = platformMMKV.allKeys() as? List ?: listOf()
179 |
180 | override fun containsKey(key: String): Boolean = platformMMKV.containsKey(key)
181 |
182 | override fun checkReSetCryptKey(key: String?) = platformMMKV.checkReSetCryptKey(key?.encodeToByteArray()?.toNSData())
183 |
184 | override fun mmapID(): String = platformMMKV.mmapID()
185 |
186 | override fun async() = platformMMKV.async()
187 |
188 | override fun sync() = platformMMKV.sync()
189 |
190 | override fun trim() = platformMMKV.trim()
191 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2022 Ctrip.com
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------