Skip to content

Instantly share code, notes, and snippets.

@rafaelbartolome
Last active September 5, 2019 06:44
Show Gist options
  • Save rafaelbartolome/0b433b8e95ea08ef596a114e6c3a575e to your computer and use it in GitHub Desktop.
Save rafaelbartolome/0b433b8e95ea08ef596a114e6c3a575e to your computer and use it in GitHub Desktop.
Use iCloud SafariKeychain from Swift5
//
// 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(()))
}
}
}
}
@gee-whiz
Copy link

gee-whiz commented Sep 4, 2019

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

@rafaelbartolome
Copy link
Author

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

@gee-whiz
Copy link

gee-whiz commented Sep 5, 2019

Thank you so much Rafael

@rafaelbartolome
Copy link
Author

Glad I could help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment