Last active
September 5, 2019 06:44
-
-
Save rafaelbartolome/0b433b8e95ea08ef596a114e6c3a575e to your computer and use it in GitHub Desktop.
Use iCloud SafariKeychain from Swift5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SafariKeychain.swift | |
// Positipp | |
// | |
// Created by Rafael Bartolome on 15/06/2019. | |
// Copyright © 2019 Moonshot People Tech SL. All rights reserved. | |
// | |
import Foundation | |
extension String: Error {} | |
typealias SafariKeychainCheckCompletion = (Result<(String?, String?), Error>) -> Void | |
typealias SafariKeychainUpdateCompletion = (Result<Void, Error>) -> Void | |
class SafariKeychain { | |
enum Constants { | |
/// Error code for when there are no accounts associated with the domain | |
static let SafariKeychainErrorCodeNoAccount = -25300 | |
/// Error code for when the entitlements aren't setup correctly. | |
static let SafariKeychainErrorCodeNoDomain = -50 | |
#warning("TODO: Update domain to your domain") | |
static let Domain = "example.com" | |
} | |
} | |
extension SafariKeychain { | |
/// Request the credentials information stored in safari keychain | |
func checkSafariCredentials(completion: @escaping SafariKeychainCheckCompletion) { | |
SecRequestSharedWebCredential(Constants.Domain as CFString?, .none) { (credentials, error) -> () in | |
var username: String? | |
var password: String? | |
if let error = error { | |
return completion(.failure(error)) | |
} | |
if let credentials = credentials { | |
let credentialsArray = credentials as NSArray | |
if credentialsArray.count > 0 { | |
if let credentials = credentialsArray[0] as? [String: String] { | |
if let account = credentials[String(kSecAttrAccount)] { | |
username = account | |
} | |
if let sharedPassword = credentials[String(kSecSharedPassword)] { | |
password = sharedPassword | |
} | |
DispatchQueue.main.async { | |
completion(.success((username, password))) | |
} | |
return | |
} | |
} | |
} else { | |
//User cancelled the operation | |
DispatchQueue.main.async { | |
completion(.failure("User cancel operation")) | |
} | |
return | |
} | |
DispatchQueue.main.async { | |
completion(.failure("General error")) | |
} | |
} | |
} | |
/// Update the credentials information in safari keychain or remove the current ones by passing a nil password. | |
func updateSafariCredentials(userName: String, | |
password: String? = nil, | |
completion: @escaping SafariKeychainUpdateCompletion) { | |
SecAddSharedWebCredential(Constants.Domain as CFString, userName as CFString, password as CFString?) { error -> () in | |
if let error = error { | |
completion(.failure(error)) | |
} else { | |
completion(.success(())) | |
} | |
} | |
} | |
} |
I have it working in production environment and works fine, but I had lots of problems with the association of the domain.
Please, check that you are following all the steps from this guide and than you have a valid SSL certificate for the domain: https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains
Thank you so much Rafael
Glad I could help.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Good day Rafael, I hope you're doing well. I'm trying to implement the ecAddSharedWebCredential on my app but it seems like it doesn't work on a real device. I keep getting this error domain(my domain) not found in com.apple.developer.associated-domains entitlement