0

I'm working through an OAuth tutorial that uses the Locksmith framework to store tokens. The tutorial is written in an older version of Swift and also with an older version of the Locksmith framework. While recreating the example in Swift 4, I noticed Locksmith 4.0 uses error handling with do try catch blocks. I'm attempting to translate the tutorial's optional binding into using the framework's error handling approach. I first tried it on my own but I'm having trouble making the mental connection of why the tutorial uses optional binding.

Here the excerpt from the tutorial:

var OAuthToken: String?
{
  set
  {
    if let valueToSave = newValue
    {
      let error = Locksmith.saveData(["token": valueToSave], forUserAccount: "github")
      if let errorReceived = error
      {
        Locksmith.deleteDataForUserAccount("github")
      }
      addSessionHeader("Authorization", value: "token \(newValue)")      
    }
    else // they set it to nil, so delete it
    {
      Locksmith.deleteDataForUserAccount("github")
      removeSessionHeaderIfExists("Authorization")
    }
  }
  get
  {
    // try to load from keychain
    let (dictionary, error) = Locksmith.loadDataForUserAccount("github")
    if let token =  dictionary?["token"] as? String {
      return token
    }
    removeSessionHeaderIfExists("Authorization")
    return nil
  }
}

Here is what I have. I believe I'm misusing the catch statement in correlation to optional binding:

var OAuthTokenCompletionHandler: ((NSError?) -> Void)?

    var authToken: String? {
        set {
            if let valueToSave = newValue{
                do{
                try Locksmith.saveData(data: ["token" : valueToSave], forUserAccount: "AzureMediaServices")
                } catch {
                    //could not save the data into keychain
                    //handle the error somehow
                    try Locksmith.deleteDataForUserAccount(userAccount: "AzureMediaServices")
                }

                addSessionHeader("Authorization", value: "Bearer \(valueToSave)")
            } else {
                //try to set it to nil
                removeSessionHeaderIfExists("Authorization")
            }
        }
        get {
            //TODO: implement
        }
    }
Laurence Wingo
  • 3,912
  • 7
  • 33
  • 61

2 Answers2

0

I think the main issue here is that the original code did not handle any error states of Locksmith.deleteDataForUserAccount. Locksmith.deleteDataForUserAccount is getting called twice, and this computed property is getting rather complex. If the error handling for both of those function calls is the same, I would recommend extracting it into a helper function.

Here's my first stab at it:

var OAuthTokenCompletionHandler: ((NSError?) -> Void)?

var authToken: String? {
    set {
        guard let valueToSave = newValue else {
            do {
                try Locksmith.deleteDataForUserAccount(userAccount: "AzureMediaServices")
            }
            catch {
                // handle the error somehow
            }

            removeSessionHeaderIfExists("Authorization")
            return
        }

        do{
            try Locksmith.saveData(data: ["token" : valueToSave], forUserAccount: "AzureMediaServices")
        } catch {
            do {
                try Locksmith.deleteDataForUserAccount(userAccount: "AzureMediaServices")
            }
            catch {
                // handle the error somehow
            }
        }

        addSessionHeader("Authorization", value: "Bearer \(valueToSave)")
    }
    get {
        guard let token = Locksmith.loadDataForUserAccount("github")?["token"] as? String {
            removeSessionHeaderIfExists("Authorization")
            return nil
        }
        return token
    }
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
0
var OAuthToken: String? {
    set {
        guard let newValue = newValue else {
            let _ = try? Locksmith.deleteDataForUserAccount(userAccount: "github")
            return
        }
        guard let _ = try? Locksmith.updateData(data: ["token": newValue],
                                                forUserAccount: "github") else {
                                                    let _ = try? Locksmith.deleteDataForUserAccount(userAccount: "github")
                                                    return
        }
    }
    get {
        // try to load from keychain
        let dictionary = Locksmith.loadDataForUserAccount(userAccount: "github")
        return dictionary?["token"] as? String
    }
}