yasuoza diary

web and life

Swift can't retrieve data from keychain on Release build

With, swift, we can(should) retrive data like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var keychainQuery: NSMutableDictionary = NSMutableDictionary(
    objects: [
      kSecClassGenericPasswordValue,
      service,
      key,
      kCFBooleanTrue,
      kSecMatchLimitOneValue
    ],
    forKeys: [
      kSecClassValue,
      kSecAttrServiceValue,
      kSecAttrAccountValue,
      kSecReturnDataValue,
      kSecMatchLimitValue
    ]
)

var dataTypeRef :Unmanaged<AnyObject>?

// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)

var contentsOfKeychain: NSString?

if status == errSecSuccess {
    let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()

    // Convert the data retrieved from the keychain into a string
    contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
}

Otherwise, on release build, swift can’t retrieve data from keychain with above code.
This is related to Swift compiler's Optimization Level.

So, first option is changing Optimization Level of Relase to be None [-Onone]. But this drops code optimization, so following code is recommended.

1
2
3
4
5
6
7
8
9
10
var result: AnyObject?
var status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(keychainQuery, UnsafeMutablePointer($0)) }

if status == errSecSeccess {
    if let data = result as NSData? {
        if let contentsOfKeychain = NSString(data: data, encoding: NSUTF8StringEncoding) {
            // ...
        }
    }
}

The point is to use withUnsafeMutablePointer, previous example we used Unmanaged.
This should solve the issue.

refs: http://stackoverflow.com/questions/26355630/swift-keychain-and-provisioning-profiles

Comments