diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt
index 133cdafd5..2e05c8369 100644
--- a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt
+++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt
@@ -1,7 +1,7 @@
 package com.clipevery.macos
 
 import com.clipevery.clip.ClipboardService
-import com.clipevery.macos.api.MacClipboard
+import com.clipevery.macos.api.MacosApi
 import java.awt.Toolkit
 import java.awt.datatransfer.Clipboard
 import java.awt.datatransfer.Transferable
@@ -22,7 +22,7 @@ class MacosClipboardService
 
     override fun run() {
         try {
-            MacClipboard.INSTANCE.clipboardChangeCount.let { currentChangeCount ->
+            MacosApi.INSTANCE.getClipboardChangeCount().let { currentChangeCount ->
                 if (changeCount == currentChangeCount) {
                     return
                 }
diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt
new file mode 100644
index 000000000..1d7f389cd
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt
@@ -0,0 +1,22 @@
+package com.clipevery.macos
+
+import com.clipevery.macos.api.MacosApi
+
+object MacosKeychainHelper {
+
+    fun getPassword(service: String, account: String): String? {
+        return MacosApi.INSTANCE.getPassword(service, account)
+    }
+
+    fun setPassword(service: String, account: String, password: String): Boolean {
+        return MacosApi.INSTANCE.setPassword(service, account, password)
+    }
+
+    fun updatePassword(service: String, account: String, password: String): Boolean {
+        return MacosApi.INSTANCE.updatePassword(service, account, password)
+    }
+
+    fun deletePassword(service: String, account: String): Boolean {
+        return MacosApi.INSTANCE.deletePassword(service, account)
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt
deleted file mode 100644
index f194412a8..000000000
--- a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.clipevery.macos.api
-
-import com.sun.jna.Library
-import com.sun.jna.Native
-
-
-interface MacClipboard : Library {
-    val clipboardChangeCount: Int
-
-    companion object {
-        val INSTANCE: MacClipboard = Native.load("ClipboardHelper", MacClipboard::class.java)
-    }
-}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt
new file mode 100644
index 000000000..adbdbeb78
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt
@@ -0,0 +1,21 @@
+package com.clipevery.macos.api
+
+import com.sun.jna.Library
+import com.sun.jna.Native
+
+interface MacosApi : Library {
+
+    fun getClipboardChangeCount(): Int
+
+    fun getPassword(service: String, account: String): String?
+
+    fun setPassword(service: String, account: String, password: String): Boolean
+
+    fun updatePassword(service: String, account: String, password: String): Boolean
+
+    fun deletePassword(service: String, account: String): Boolean
+
+    companion object {
+        val INSTANCE: MacosApi = Native.load("MacosApi", MacosApi::class.java)
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib b/composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib
new file mode 100755
index 000000000..9ab0371f2
Binary files /dev/null and b/composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib differ
diff --git a/composeApp/src/desktopMain/swift/MacosApi.swift b/composeApp/src/desktopMain/swift/MacosApi.swift
new file mode 100644
index 000000000..4c0c1665e
--- /dev/null
+++ b/composeApp/src/desktopMain/swift/MacosApi.swift
@@ -0,0 +1,93 @@
+import Cocoa
+import Foundation
+import Security
+
+@_cdecl("getClipboardChangeCount")
+public func getClipboardChangeCount() -> Int {
+    let pasteboard = NSPasteboard.general
+    return pasteboard.changeCount
+}
+
+@_cdecl("getPassword")
+public func getPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> UnsafePointer<CChar>? {
+    let serviceString = String(cString: service)
+    let accountString = String(cString: account)
+
+    let query: [String: Any] = [
+        kSecClass as String: kSecClassGenericPassword,
+        kSecAttrService as String: serviceString,
+        kSecAttrAccount as String: accountString,
+        kSecReturnData as String: kCFBooleanTrue!,
+        kSecMatchLimit as String: kSecMatchLimitOne
+    ]
+
+    var item: CFTypeRef?
+    let status = SecItemCopyMatching(query as CFDictionary, &item)
+
+    guard status == errSecSuccess else { return nil }
+
+    if let data = item as? Data, let password = String(data: data, encoding: .utf8) {
+        return UnsafePointer<CChar>(strdup(password))
+    }
+
+    return nil
+}
+
+@_cdecl("setPassword")
+public func setPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, password: UnsafePointer<CChar>) -> Bool {
+    let serviceString = String(cString: service)
+    let accountString = String(cString: account)
+    let passwordString = String(cString: password)
+
+    let passwordData = passwordString.data(using: .utf8)!
+
+    let query: [String: Any] = [
+        kSecClass as String: kSecClassGenericPassword,
+        kSecAttrService as String: serviceString,
+        kSecAttrAccount as String: accountString,
+        kSecValueData as String: passwordData
+    ]
+
+    // Try to add the item to the keychain
+    let status = SecItemAdd(query as CFDictionary, nil)
+
+    // Check the result
+    return status == errSecSuccess
+}
+
+@_cdecl("updatePassword")
+public func updatePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, newPassword: UnsafePointer<CChar>) -> Bool {
+    let serviceString = String(cString: service)
+    let accountString = String(cString: account)
+    let newPasswordString = String(cString: newPassword)
+
+    let query: [String: Any] = [
+        kSecClass as String: kSecClassGenericPassword,
+        kSecAttrService as String: serviceString,
+        kSecAttrAccount as String: accountString
+    ]
+
+    let updateFields: [String: Any] = [
+        kSecValueData as String: newPasswordString.data(using: .utf8)!
+    ]
+
+    let status = SecItemUpdate(query as CFDictionary, updateFields as CFDictionary)
+
+    return status == errSecSuccess
+}
+
+@_cdecl("deletePassword")
+public func deletePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> Bool {
+    let serviceString = String(cString: service)
+    let accountString = String(cString: account)
+
+    let query: [String: Any] = [
+        kSecClass as String: kSecClassGenericPassword,
+        kSecAttrService as String: serviceString,
+        kSecAttrAccount as String: accountString
+    ]
+
+    let status = SecItemDelete(query as CFDictionary)
+
+    return status == errSecSuccess
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt b/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt
new file mode 100644
index 000000000..259520de0
--- /dev/null
+++ b/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt
@@ -0,0 +1,24 @@
+package com.clipevery.macos
+
+import com.clipevery.platform.currentPlatform
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class MacosKeychainHelperTest {
+
+    @Test
+    fun testKeychain() {
+        if (currentPlatform().isMacos()) {
+            MacosKeychainHelper.setPassword("com.clipevery", "test", "test")
+            var password = MacosKeychainHelper.getPassword("com.clipevery", "test")
+            assertEquals("test", password)
+            MacosKeychainHelper.updatePassword("com.clipevery", "test", "test1")
+            password = MacosKeychainHelper.getPassword("com.clipevery", "test")
+            assertEquals("test1", password)
+            assertTrue(MacosKeychainHelper.deletePassword("com.clipevery", "test"))
+            val password1 = MacosKeychainHelper.getPassword("com.clipevery", "test")
+            assertTrue(password1 == null)
+        }
+    }
+}
\ No newline at end of file