Skip to content
This repository has been archived by the owner on Aug 24, 2019. It is now read-only.

Swift 2.0 Error Handling Compatibility #112

Closed
lgauthier opened this issue Sep 17, 2015 · 8 comments
Closed

Swift 2.0 Error Handling Compatibility #112

lgauthier opened this issue Sep 17, 2015 · 8 comments

Comments

@lgauthier
Copy link
Contributor

Right now, there are compatibility issues with Swift 2.0's new error handling system. With the new system, legacy code that follows Cocoa's inout NSError pattern are automatically converted into methods that throw and don't include the inout NSError parameter.

I think the issue that SSKeychain is suffering from stems from the fact that many of the methods provide an alternative method signature without the inout NSError parameter. For example:

+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

For Swift 2.0, these methods get converted to:

class func passwordForService(serviceName: String!, account: String!) -> String!
class func passwordForService(serviceName: String!, account: String!, error: ()) throws -> String

So, if you exclude the error parameter in your method call, the compiler will match the method signature with the first method which doesn't throw. I found that I can actually call the second method by including the error parameter with an argument of (). However, Xcode still gives the warning, "No calls to throwing functions occur within 'try' expression."

I think the best solution (for now) might be to include an annotation in the method declaration similar to the LocalAuthentication framework. An example from the LocalAuthentication framework:

- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError * __autoreleasing *)error __attribute__((swift_error(none)));

This causes the method to be converted to:

public func canEvaluatePolicy(policy: LAPolicy, error: NSErrorPointer) -> Bool

This allows the method to be called with the familiar inout NSError syntax. So the SSKeychain methods could be declared as:

+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));

I haven't tested this yet, but I wanted to at least bring it to your attention as early as possible.

lgauthier added a commit to lgauthier/sskeychain that referenced this issue Sep 17, 2015
soffes added a commit that referenced this issue Sep 17, 2015
Fixes issue #112 involving Swift 2.0 error handling compatibility.
@Razinsky
Copy link

Thanks @lgauthier for the update... Can you give an example of how to implement the new error handling method for SSKeychain with Swift 2.0?

@lgauthier
Copy link
Contributor Author

@Razinsky with the change I added, you would use the methods the same way as you normally would with the inout NSError pointer:

var error: NSError?
let password = SSKeychain.passwordForService("AService", account: "AnAccount", error: &error)

if let error = error {
    // Handle error
}

In order to use the new Swift 2.0 error handling mechanism, the method declaration that doesn't include the inout NSError pointer would need to be removed. If it's removed, then that would allow this method:

+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

To be converted to this in Swift 2.0:

class func passwordForService(serviceName: String!, account: String!) throws -> String

Then, you could use the method like this:

do {
    let password = try SSKeychain.passwordForService("AService", account: "AnAccount", error: &error)
}
catch let error as NSError {
    // Handle error
}

Unfortunately, if we just removed the method declarations that don't include the inout NSError parameter, we'd lose backwards compatibility for Objective-C code that calls those methods. So, it might be best to remove those method declarations in the next major release.

@Razinsky
Copy link

Is it possible that the same changes should apply to methods in SSKeychainQuery?

@lgauthier
Copy link
Contributor Author

Though SSKeychainQuery does have methods with an inout NSError parameter, it doesn't have the conflict issue because there aren't alternative versions of the methods that exclude the inout NSError parameter. For those methods, you can use the do-try-catch syntax in Swift 2.0:

do {
    try query.save()
}
catch {
    // Handle error
}

@Razinsky
Copy link

Thanks a lot man!

@lgauthier
Copy link
Contributor Author

No problem!

@davebang
Copy link

I may be missing something but this solution isn't working for me...

        var error: NSError?
        var login: String? = SSKeychain.passwordForService(kMyAppService, account:kLoginUserNamePreference, error:&error)

        if let _ = error {
            // handle
        }

Results in....

'&' used with non-inout argument of type '()'

1 inouterror

@davebang
Copy link

Method declaration is...

+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants