Adding item to keychain using Swift - ios

I'm trying to add an item to the iOS keychain using Swift but can't figure out how to type cast properly. From WWDC 2013 session 709, given the following Objective-C code:
NSData *secret = [#"top secret" dataWithEncoding:NSUTF8StringEncoding];
NSDictionary *query = #{
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: #"myservice",
(id)kSecAttrAccount: #"account name here",
(id)kSecValueData: secret,
};
OSStatus = SecItemAdd((CFDictionaryRef)query, NULL);
Attempting to do it in Swift as follows:
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "MyService",
kSecAttrAccount: "Some account",
kSecValueData: secret
]
yields the error "Cannot convert the expression's type 'Dictionary' to 'DictionaryLiteralConvertible'.
Another approach I took was to use Swift and the - setObject:forKey: method on a Dictionary to add kSecClassGenericPassword with the key kSecClass.
In Objective-C:
NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
In the Objective-C code, the CFTypeRef of the various keychain item class keys are bridged over using id. In the Swift documentation it's mentioned that Swift imports id as AnyObject. However when I attempted to downcast kSecClass as AnyObject for the method, I get the error that "Type 'AnyObject' does not conform to NSCopying.
Any help, whether it's a direct answer or some guidance about how to interact with Core Foundation types would be appreciated.
EDIT 2
This solution is no longer valid as of Xcode 6 Beta 2. If you are using Beta 1 the code below may work.
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let query = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "Some account", secret], forKeys: [kSecClass,kSecAttrService, kSecAttrAccount, kSecValueData])
OSStatus status = SecItemAdd(query as CFDictionaryRef, NULL)
To use Keychain Item Attribute keys as dictionary keys you have to unwrap them by using either takeRetainedValue or takeUnretainedValue (as appropriate). Then you can cast them to NSCopying. This is because they are CFTypeRefs in the header, which aren't all copyable.
As of Xcode 6 Beta 2 however, this causes Xcode to crash.

You simply need to downcast the literal:
let dict = ["hi": "Pasan"] as NSDictionary
Now dict is an NSDictionary. To make a mutable one, it's very similar to Objective-C:
let mDict = dict.mutableCopy() as NSMutableDictionary
mDict["hola"] = "Ben"

In the xcode 6.0.1 you must do this!!
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)

Perhaps things have improved since. On Xcode 7 beta 4, no casting seems to be necessary except when dealing with the result AnyObject?. Specifically, the following seems to work:
var query : [NSString : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : "MyAwesomeService",
kSecReturnAttributes : true, // return dictionary in result parameter
kSecReturnData : true // include the password value
]
var result : AnyObject?
let err = SecItemCopyMatching(query, &result)
if (err == errSecSuccess) {
// on success cast the result to a dictionary and extract the
// username and password from the dictionary.
if let result = result as ? [NSString : AnyObject],
let username = result[kSecAttrAccount] as? String,
let passdata = result[kSecValueData] as? NSData,
let password = NSString(data:passdata, encoding:NSUTF8StringEncoding) as? String {
return (username, password)
}
} else if (status == errSecItemNotFound) {
return nil;
} else {
// probably a program error,
// print and lookup err code (e.g., -50 = bad parameter)
}
To add a key if it was missing:
var query : [NSString : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : "MyAwesomeService",
kSecAttrLabel : "MyAwesomeService Password",
kSecAttrAccount : username,
kSecValueData : password.dataUsingEncoding(NSUTF8StringEncoding)!
]
let result = SecItemAdd(query, nil)
// check that result is errSecSuccess, etc...
A few things to point out: your initial problem might have been that str.dataUsingEncoding returns an Optional. Adding '!' or better yet, using an if let to handle nil return, would likely make your code work. Printing out the error code and looking it up in the docs will help a lot in isolating the problem (I was getting err -50 = bad parameter, until I noticed a problem with my kSecClass, nothing to do with data types or casts!).

This seemed to work fine or at least compiler didn't have kittens - UNTESTED beyond that
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var array1 = NSArray(objects:"\(kSecClassGenericPassword)", "MyService", "Some account", secret)
var array2 = NSArray(objects:"\(kSecClass)","\(kSecAttrService)", "\(kSecAttrAccount)","\(kSecValueData)")
let query = NSDictionary(objects: array1, forKeys: array2)
println(query)
let status = SecItemAdd(query as CFDictionaryRef, nil)
Seems to work fine in Beta 2

In order to get this to work, you need to access the retained values of the keychain constants instead. For example:
let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString
You can then reference the values in the MSMutableDictionary like so:
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
I wrote a blog post about it at:
http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/
Hope this helps!
rshelby

Swift 3
let kSecClassKey = String(kSecClass)
let kSecAttrAccountKey = String(kSecAttrAccount)
let kSecValueDataKey = String(kSecValueData)
let kSecClassGenericPasswordKey = String(kSecClassGenericPassword)
let kSecAttrServiceKey = String(kSecAttrService)
let kSecMatchLimitKey = String(kSecMatchLimit)
let kSecReturnDataKey = String(kSecReturnData)
let kSecMatchLimitOneKey = String(kSecMatchLimitOne)
you can also do it inside the dictionary itself alá:
var query: [String: Any] = [
String(kSecClass): kSecClassGenericPassword,
String(kSecAttrService): "MyService",
String(kSecAttrAccount): "Some account",
String(kSecValueData): secret
]
however, this is more expensive for the compiler, even more so since you're probably using the query in multiple places.

more convenient to use the cocoa pods SSKeychain
+ (NSArray *)allAccounts;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;

Related

Apple in app provisioning 'Could not add card'

I am implementing apple in-app provisioning and I follow all steps in the apple guide but in the end, I get a message 'Could not add card' but don't have any error throw this process.
This is how I create PKAddPaymentPassViewController
let cardInfoPass = PKAddPaymentPassRequestConfiguration.init(encryptionScheme: PKEncryptionScheme.ECC_V2);
cardInfoPass?.cardholderName = cardholderName as? String; //The name of the person as shown on the card.
cardInfoPass?.primaryAccountSuffix = primaryAccountSuffix as? String; //The last four or five digits of the card’s number.
cardInfoPass?.localizedDescription = localizedDescription as? String; //A short description of the card.
cardInfoPass?.paymentNetwork = PKPaymentNetwork.masterCard;
cardInfoPass?.primaryAccountIdentifier = primaryAccountIdentifier as? String; // A primary account identifier, used to filter out pass libraries.
cardholderName is the name written on the card
primaryAccountSuffix last 4 digit written on the card
localizedDescription bank name
paymentNetwork we are using master card
primaryAccountIdentifier it is number from iTunes something light this 1MNJDDA667.com.bank.package.name
I think this part is correct I can open the apple wallet modal and all this data are there but when I continue in a modal on the end I need to get certificate and send this certificate to our BE and be should send me back 3 values and they send it to me
...
let certificateLeaf = certificates[0].base64EncodedString();
let certificateSubCA = certificates[1].base64EncodedString();
let nonceString = nonce.base64EncodedString();
let nonceSignature = nonceSignature.base64EncodedString();
...
let reqDataDic: [String: Any] = [
"cardId": cardId,
"applePublicCertificate": certificateSubCA,
"nonce": nonceString,
"nonceSignature": nonceSignature,
"customerId": customerId,
"deviceId": deviceId,
]
....
var request = URLRequest(url: url)
request.httpMethod = "POST"
....
request.httpBody = try? JSONSerialization.data(withJSONObject: reqDataDic, options: .prettyPrinted)
UPDATE2: we are now sending nonce and nonceSignature as HEX like this
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}
...
let nonceData = Data(bytes: nonce)
let nonceHex = nonceData.hexEncodedString();
let nonceSignatureData = Data(bytes: nonceSignature)
let nonceSignatureHex = nonceSignatureData.hexEncodedString();
BE send me back all values that I need: activationData, ephemeralPublicKey, encryptedPassData it returns it as a JSON object so I need to convert it to Data and all these values put into handler
this is how I am putting data to handler:
if let dictionaryJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any] {
let activationDataString = dictionaryJson["activationData"] as! String;
let ephemeralPublicKeyString = dictionaryJson["ephemeralPublicKey"] as! String;
let encryptedPassDataString = dictionaryJson["encryptedPassData"] as! String;
let activationData = activationDataString.data(using: .utf8)
let ephemeralPublicKey = Data(base64Encoded: ephemeralPublicKeyString)
let encryptedPassData = Data(base64Encoded: encryptedPassDataString)
let paymentPassRequest = PKAddPaymentPassRequest.init()
paymentPassRequest.activationData = activationData;
paymentPassRequest.encryptedPassData = encryptedPassData;
paymentPassRequest.ephemeralPublicKey = ephemeralPublicKey;
handler(paymentPassRequest)
}
I fill all data into paymentPassRequest and all looks ok xCode is not complaining.
And at this moment apple wallet shows an alert dialog with Could not add a card with 2 buttons try it later or try it again ....
I have a card whitelisted on the MasterCard side
I tried it on simulators, real devices, and also on app in TestFlight
UPDATE:
We found an error from the Apple
Response:
https://nc-pod4-smp-device.apple.com:443/broker/v4/devices/042D1xxxxxxxxxxxxx2C52/cards 500
{
Connection = close;
"Content-Length" = 81;
"Content-Type" = "application/json";
Date = "Thu, 08 Jul 2021 08:35:25 GMT";
Vary = "accept-language";
"X-Pod" = "nc-pod4";
"X-Pod-Region" = "paymentpass.com.apple";
"x-conversation-id" = b2axxxxxxxxxxx9e6a4d;
}
{
statusCode = 500;
statusMessage = "Broker Service Response exception";
}
you are encoding nonce, nonce signature with Hex format for sending it to your server, and after getting the response back, you are trying to convert them with base64 and utf8. Try with Hex, it should work.
We are using the below conversions
- (NSData *)dataFromHexString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
int length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
-(NSMutableString *) convertToString:(NSData *)data{
NSUInteger capacity = data.length * 2;
NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity];
const unsigned char *buf = data.bytes;
NSInteger i;
for (i=0; i<data.length; ++i) {
[sbuf appendFormat:#"%02x", (NSUInteger)buf[i]];
}
return sbuf;
}
let activationData = activationDataString.data(using: .utf8)
I think this encoded is only for VISA.
For MasterCard it has to be base64:
let activationData = Data(base64Encoded: activationDataString)
I ran into this exact issue implementing our Card Provisioning. In our case we had to do both of the following:
Make sure our PKAddPaymentPassRequest fields were all set properly. All three of the fields we were provided by our API (activationData, encryptedPassData, ephemeralPublicKey) were base64 encoded so they all had to be converted to Data's as such: paymentPassRequest.ephemeralPublicKey = Data(base64Encoded: <YOUR EPHEMERAL PUBLIC KEY STRING>, options: [])
We had to create new TestFlight builds in order to fully test this workflow. I ran into that exact same 500 response from Apple anytime I tried profiling or running the app from Xcode directly, even if it was on a physical device. It wasn't until I ran the TestFlight build that it finally worked properly.

Swift - Cannot invoke initializer for type 'NSDictionary'

Since upgrading to Xcode7, I am getting the following error:
Cannot invoke initializer for type 'NSDictionary' with an argument list of type '(objects: [AnyObject!], forKeys: [String])'
on this line of code:
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"]))
Can someone explain why?
EDIT: Here is the full block of code
if(self.bidTextField.text!.rangeOfString("^[0-9]*$", options: .RegularExpressionSearch) != nil) {
self.sessionBids = array[0].objectForKey("bids") as? NSMutableArray
var lastSessionBid : NSDictionary
SVProgressHUD.showProgress(50)
var previousHighBid : Int! = 0
if(self.sessionBids == nil) {
self.sessionBids = NSMutableArray()
} else {
lastSessionBid = self.sessionBids.objectAtIndex(self.sessionBids.count - 1) as! NSDictionary
previousHighBid = Int(lastSessionBid.objectForKey("bid") as! String)
}
if( previousHighBid >= Int(self.bidTextField.text!)) {
print("bid is lower than current bid")
SVProgressHUD.showErrorWithStatus("Bid is lower than current bid!")
return
} else {
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"]))
SVProgressHUD.showProgress(75)
self.session.setObject(self.sessionBids, forKey: "bids")
self.session.save()
self.keyboardShowing = false
self.reloadSessionBids()
SVProgressHUD.showProgress(100)
SVProgressHUD.showSuccessWithStatus("Successfully Added Bid")
}
} else {
SVProgressHUD.showErrorWithStatus("Bid must be a number!")
}
You're trying to call init(objects: [AnyObject], forKeys keys: [NSCopying]) initializer of NSDictionary. objects: [AnyObject] can't contain Optionals (according to its declaration), and it seems that PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text are all Optionals, that's why you're getting the error.
To resolve this, as vadian suggested, you'll need to unwrap all the Optionals in that array.
Both the objects and the keys of a dictionary must not be nil.
Make sure that all objects are non-optionals.
Try calling NSDictionary's
public init(objects: UnsafePointer<AnyObject?>, forKeys keys: UnsafePointer<NSCopying?>, count cnt: Int) instead:
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"], count: 3))
Alternatively, try using a native dictionary instead of an NSDictionary.
Say:
var bidDict = [String : AnyObject]()
bid["user"] = PFUser.currentUser().objectId ?? "unknown"
bid["name"] = PFUser.currentUser().objectForKey("username") ?? "unknown"
bid["bid"] = self.bidTextField.text ?? "unknown"
Then insert into your array:
self.sessionBids!.addObject(bid)

TouchID with KeyChain - Enter Passcode screen appears malformed (Swift)

I am having a hard time with this one:
Our app uses TouchID to retrieve secrets from the KeyChain that are used to authenticate to a WebApp in a WKWebView. All is fine when the user successfully authenticates with TouchID.
The problem arises when the user fails TouchID and then taps on "Enter Passcode" to authenticate via device passcode instead. I noticed that the screen that gets presented is plain white, and the passcode text-box does not show a value for the last character entered until it is masked, creating a strange user experience. This seems like a bug to me, unless I am somehow missing a configuration requirement.
// global arguments for the touchId keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
let kSecAttrAccessControlValue = NSString(format: kSecAttrAccessControl)
let kSecUseOperationPromptValue = NSString(format: kSecUseOperationPrompt)
// set access control
let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .UserPresence, nil)
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue, "Place your finger to authenticate.", accessControl.takeUnretainedValue()], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue, kSecUseOperationPromptValue, kSecAttrAccessControlValue])
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain item
touchIdStatusCode = SecItemCopyMatching(keychainQuery, &dataTypeRef)
Here is the generated keychainQuery dictionary:
{
"accc" = "<SecAccessControlRef: 0x170621140>";
"acct" = “MY_ACCOUNT”;
"class" = genp;
"m_Limit" = "m_LimitOne";
"r_Data" = 1;
"svce" = “MY_SERVICE”;
"u_OpPrompt" = "Place your finger to authenticate.";
}
This behavior exists on the latest iOS version (8.4). Here is the screen I was referring to:
My question is: Am I somehow causing the screen to be broken by the way I am invoking TouchID? Or is this perfectly normal for this scenario?
Thanks, and sorry for the lengthy post!
The issue described is a bug that was confirmed by Apple engineers. It still exists as of iOS 9.0.2.

How to parse NSMutableDictionary in Swift programming?

In my apple watch interface controller "reply" object contains Json response in NSDictionary. Here is my response i need the "accountName" from this below response. How to parse it in Swift programming.
[Accounts: (
{
accountName = "ABCD";
idNumber = 114000093;
email = "xyz#gmail.com";
index = 0;
nickName = "Suites";
},
{
accountName = "EFGH";
idNumber = 114000094;
email = "abc#gmail.com";
index = 1;
nickName = "Sultan";
}
)]
I have tried like below:
WKInterfaceController.openParentApplication(["request" : "GetData"], reply: { (reply, error) -> Void in
println(reply)
self.accountNames = reply["Accounts"] as? NSMutableArray
println(self.accountNames)
This is very easy, you can try out by your own, and can google also, there are lots and lots of example available, you just need to brush up little bit.
Btw, You can try like this,
let accountArray : NSArray = reply.objectForKey("Accounts") as! NSArray
let accountDic : NSDictionary = accountArray.objectAtIndex(0) as! NSDictionary
let accountName : NSString = accountDic.valueForKey("accountName") as! NSString
Hope, this helps you.
Try this :
let accountInfos = (reply.valueForKey("Accounts") as [NSDictionary]).map {
YourModelClass(accountName: $0["accountName"] as String, idNumber: $0["idNumber"] as String ..... same for all other)
}
self.yourMutableArray.addObjectsFromArray(accountInfos)
let accountName = (yourMutableArray.objectAtIndex(0)as YourModelClass).accountName
println(accountName)
And your model class :
class YourModelClass : NSObject {
var accountName : String?
.
.
init(accountName:String, ......) {
self.accountName = accountName
}

Querying iOS Keychain using Swift

I'm stuck converting the Keychain query result using Swift.
My request seems to be working:
let queryAttributes = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "MyAccount", true],
forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData])
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
var dataTypeRef : Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);
let retrievedData : NSData = dataTypeRef!.takeRetainedValue() as NSData
*** ^^^^can't compile this line^^^^
})
My problem is, code won't compile:
Bitcast requires both operands to be pointer or neither
%114 = bitcast %objc_object* %113 to %PSs9AnyObject_, !dbg !487
Bitcast requires both operands to be pointer or neither
%115 = bitcast %PSs9AnyObject_ %114 to i8*, !dbg !487
LLVM ERROR: Broken function found, compilation aborted!
I don't know how to convert Unmanaged<AnyObject> to NSData.
Any ideas?
Use withUnsafeMutablePointer function and UnsafeMutablePointer struct to retrieving the data, such as the following:
var result: AnyObject?
var status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(queryAttributes, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let data = result as NSData? {
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) {
// ...
}
}
}
it works fine with release (Fastest [-O]) build.
Looks like you have hit a compiler bug, which you should report. You can take a different path to retrieving the value, such as the following:
var dataTypeRef :Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
}
The error manifests itself when using AnyObject as a type parameter T of Unmanaged<T>. The following code snippet compiles without issue, which uses a more specific type, CFError:
let url = NSURL(string:"dummy")
var errorRef: Unmanaged<CFError>?
let succeeded = CTFontManagerRegisterFontsForURL(url, .Process, &errorRef)
if errorRef {
let error = errorRef!.takeRetainedValue()
}
As the Keychain API returns a different result depending of the query attributes, the use of AnyObject is required.
To get this to work you need to access the retained value for each keychain constant first. For example:
let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString
You'll then need to reference the constant you created in the keychain dictionary object like so.
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
I wrote a blog post about it at: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/
Hope this helps!
rshelby

Resources