├── 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 | --------------------------------------------------------------------------------