1

I am writing an OS X app that should maintain a custom Keychain, I am trying to use the Security framework's API to create the Keychain, however, I can't seem to get it to compile under Swift.

Here's what I have, assume that path contains a path to a potentially existing Keychain:

let pathName = (path as NSString).UTF8String
var keychain: Unmanaged<SecKeychain>?

var status = withUnsafeMutablePointer(&keychain) { pointer in
    SecKeychainOpen(pathName, pointer)
}

if status != errSecSuccess {
    status = withUnsafeMutablePointer(&keychain) { pointer in
        SecKeychainCreate(pathName, UInt32(0), nil, false, nil, pointer)
    }
}

The compiler is complaining about the types in the SecKeychainCreate call, however, I fail to understand what am I doing wrong.

Cannot invoke 'withUnsafeMutablePointer' with an argument list of type '(inout Unmanaged<SecKeychain>?, (_) -> _)'

If I modify the second closure slightly, I get this compiler error:

Cannot invoke 'SecKeychainCreate' with an argument list of type '(UnsafePointer<Int8>, UInt32, nil, Bool, nil, (UnsafeMutablePointer<Unmanaged<SecKeychain>?>))'

I appreciate all suggestions.

1 Answer 1

5
+50

The promptUser parameter of SecKeychainCreate() has the type Boolean, which is a "Mac OS historic type" and an alias to UInt8, so it is different from the Swift Bool in Swift 1.2. (Compare Type 'Boolean' does not conform to protocol 'BooleanType' for a similar issue.) This means that you have to pass Boolean(0) instead of false:

SecKeychainCreate(pathName, UInt32(0), nil, Boolean(0), nil, pointer)

Additional remarks:

  • withUnsafeMutablePointer() is not needed, you can pass &keychain to the keychain functions.
  • (path as NSString).UTF8String is not needed, you can pass a Swift string to a C function expecting a const char * parameter, compare String value to UnsafePointer<UInt8> function parameter behavior.
  • Passing nil as password to SecKeychainCreate() is only allowed if promptUser is TRUE, otherwise it causes a "parameter error (-50)".
  • SecKeychainOpen() succeeds even if the keychain file does not exists. According to the documentation, you have to check SecKeychainGetStatus(). Alternatively, you can try to create the keychain file first, as for example in Open Local Items Keychain?.

Together:

let path = "/path/to/my.keychain"
var keychain: Unmanaged<SecKeychain>?

var status = SecKeychainCreate(path, 0, "", Boolean(0), nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
    status = SecKeychainOpen(path, &keychain)
}

As of Swift 2 / Xcode 7 beta 5, the Mac type Boolean is mapped to Swift as Bool, and the key chain functions do no longer return unmanaged objects:

let path = "/path/to/my.keychain"
var keychain: SecKeychain?

var status = SecKeychainCreate(path, 0, "", false, nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
    status = SecKeychainOpen(path, &keychain)
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the in-depth analysis, as I suspected it was something easy to miss. I wish the compiler would've been more specific about the error...
@HenriNormak: You are welcome. – And yes, wrong argument types can be difficult to detect, and even more so inside closures. There is room for improvement for the Swift compiler! Sometimes it helps to use the auto-completion for methods (or jump to definition) and inspect each parameter.
You mention the documentation describes how you must use SecKeychainGetStatus() since SecKeychainOpen() always succeeds. Where did you find this in the docs? I discovered this to be true empirically, but couldn't find any official reference to this behavior.
@drootang: If you command-click on SecKeychainOpen in the Xcode source editor to jump to the API documentation then you'll find the text "This keychain might not currently exist, use SecKeychainGetStatus if you want to confirm the existence of this keychain." – I don't know why that information is missing in the online docs.
@MartinR: Thanks. I suppose its a good lesson that I should always check the header comments in addition to the documentation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.