在我的应用程序中,我使用 Apple 登录,效果很好。 检查清单:
登录效果很好。
但是,在重新启动时,当我想检查登录状态时,我执行此代码
private func checkLoginStatus() {
Task {
do {
let appleIDProvider = ASAuthorizationAppleIDProvider()
let credentialState = try await appleIDProvider.credentialState(forUserID: KeychainItem.currentUserIdentifier)
if credentialState == .authorized {
withAnimation {
self.isLoggedIn = true
}
}
} catch {
fatalError(error.localizedDescription)
}
}
}
此代码总是抛出:
线程 5:致命错误:操作无法完成。 (com.apple.AuthenticationServices.AuthorizationError 错误 1000。)
然而,在此之前不久,我得到:
凭证状态请求返回错误:错误域=AKAuthenticationError Code=-7091“(null)”
该解决方案主要复制自 Apple 的 Juice Sample App。 我不知道这里出了什么问题。任何帮助将不胜感激。
我犯了一个愚蠢的错误,没有检查 Juice 示例项目中 Keychain 项的实现。
如果有人遇到此示例项目的问题,我建议在钥匙串项中寻找答案。
这是一个非常简单的会话管理器实现:
import AuthenticationServices
class SessionManager {
static let shared = SessionManager()
private init() {
// deleteKeychainItem(service: "your.bundle", account: "appleIDToken") // If your data is corrupted
}
func saveToKeychain(appleIDCredential: ASAuthorizationAppleIDCredential) {
do {
let encodedCredential = try NSKeyedArchiver.archivedData(withRootObject: appleIDCredential, requiringSecureCoding: true)
let keychainQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "your.bundle",
kSecAttrAccount as String: "appleIDToken",
kSecValueData as String: encodedCredential,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
if status == errSecSuccess {
print("Token stored in Keychain successfully")
} else if status == errSecDuplicateItem {
print("Token already exists in Keychain")
} else {
print("Error storing token in Keychain with status: \(status)")
// Handle the error appropriately
}
} catch {
print("Error encoding credential: \(error)")
}
}
func retrieveFromKeychain() -> ASAuthorizationAppleIDCredential? {
let keychainQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "your.bundle",
kSecAttrAccount as String: "appleIDToken",
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(keychainQuery as CFDictionary, &result)
if status == errSecSuccess, let tokenData = result as? Data {
do {
if let credential = try NSKeyedUnarchiver.unarchivedObject(ofClass: ASAuthorizationAppleIDCredential.self, from: tokenData) {
return credential
} else {
print("Credential could not be unarchived")
}
} catch {
print("Error decoding credential: \(error)")
}
} else if status == errSecItemNotFound {
print("Token not found in Keychain")
} else {
print("Error retrieving token from Keychain with status: \(status)")
}
return nil
}
func deleteKeychainItem(service: String, account: String) {
let keychainQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
]
let status = SecItemDelete(keychainQuery as CFDictionary)
if status == errSecSuccess {
print("Keychain item deleted successfully")
} else if status == errSecItemNotFound {
print("Keychain item not found")
} else {
print("Error deleting Keychain item with status: \(status)")
}
}
}