-1

Using Xcode 7 beta, Swift 2.0

I'm saving and loading credentials to keychain, somehow when loading I get "Optional(value)" back, it looks like this is really part of the string as it also displayed like so in a textbox or when sending to API

This is how I save and load credentials now, as you see I've done a lot of extra nil checking to make sure it is not nil or Optional, it is indeed a overuse of explanation marks...

func SaveCredentials(credentials : [String : String!]!) -> Bool
{
    if(credentials.count == 2)
    {
        //only proceed when we have two keys:  username and password
        let username = credentials["username"]
        let password = credentials["password"]

        if let usernameStr = username
        {//also tried username!=nil && password != nil
            if let passwordStr = password
            { //usernameStr and passwordStr is of type String!
                let NsDataUsername = usernameStr!.dataUsingEncoding(NSUTF8StringEncoding)
                let NsDataPassword = passwordStr!.dataUsingEncoding(NSUTF8StringEncoding)
                if(NsDataUsername != nil && NsDataPassword != nil)
                {
                    LocalStorage.saveToKeyChain("username", data: NsDataUsername!)
                    LocalStorage.saveToKeyChain("password", data: NsDataPassword!)
                    return true
                }
            }

        }
    }
    return false
}

func LoadCredentials() -> [String : String!]?
{
    let NsDataUsername = LocalStorage.loadFromKeyChain("username")
    let NsDataPassword = LocalStorage.loadFromKeyChain("password")
    if(NsDataUsername != nil && NsDataPassword != nil)
    {
        let username : String! = String(NSString(data: NsDataUsername!, encoding: NSUTF8StringEncoding))
        let password : String! = String(NSString(data: NsDataPassword!, encoding: NSUTF8StringEncoding))
        if let usernameStr = username
        {
            if let passwordStr = password
            { // password is of type String!, passwordStr is of type String
                var credentials : [String: String!] = [String : String]()
                credentials["username"] = usernameStr
                credentials["password"] = passwordStr
                return credentials
            }
        }
    }
    return nil
}

And when I send to Api, this is my method that also requires a non-optional string. This method does work when logging in, getting strings from text fields, but does not filter out that Optional when coming from keychain.

func LoginUser(email : String!, password : String!)
{
    print("LoginUser(email : \(email), password: \(password))")
    var parameters = [String : AnyObject]()
    parameters["UserName"] = email
    parameters["Password"] = password
   ......

The strings that I send to the SaveCredentials method, are the same that the user logged in with:

func LoginLocalAccount(email : String!, password : String!)
{
    databaseAPI.LoginUser(email!, password: password!) //login goes just fine
    saveCredentials(email!, password: password!) //manages to get Optional in it..
}

I suspect it has something to do with saving and loading from keychain, for interests, this is what I use to save and load from keychain.

I want to get rid of them because when the app starts, it loads the credentials and tries to login at my API. Ofcourse I get an error back that the username is not a valid e-mail, because it is Optional(email@adress.com)

CularBytes
  • 9,924
  • 8
  • 76
  • 101
  • First, these are **NOT** non-optional. They are implicitly unwrapped optionals (which are definitely optionals which can definitely be `nil`). Second, just because `Optional(email@address.com)` *prints* with the `print` function does *not* mean that it shows up anywhere else. – nhgrif Jul 13 '15 at 12:44
  • But thats the problem, it does, even when I show them in the textbox again, it shows Optional – CularBytes Jul 13 '15 at 12:47
  • Then that's the actual value in the string. – nhgrif Jul 13 '15 at 12:48
  • Yes, I noticed, so my guess is, that something goes wrong when saving, any chance you can have a look at my code and see what I'm doing wrong? – CularBytes Jul 13 '15 at 12:49
  • 1
    1st step is eliminating every single !. You don’t need any of them - neither the implicit optional types (should probably be non-optional, or possibly `?`) and the force-unwraps. Read through [this](http://stackoverflow.com/q/27622871/3925941) and [this](http://stackoverflow.com/q/29717210/3925941) to get a good idea of optionals and how to handle them, then rewrite your code getting rid of every instance of `!`. Once you’ve done that, you should have a better understanding of where the problem lies. – Airspeed Velocity Jul 13 '15 at 13:09
  • @AirspeedVelocity Thanks for those links, helped me to better understand the concept. This was indeed a overuse of !, no production code at all, was just figuring out where it went wrong. – CularBytes Jul 13 '15 at 14:12

1 Answers1

3

You're overusing !. You don't need them. Try to learn more about implicitly unwrapped optionals, optionals, ... Your code is a mess (no offense, everybody's learning).

Back to your optional problem, it's caused by this line:

let username : String! = String(NSString(data: NsDataUsername!, encoding: NSUTF8StringEncoding))

convenience init?(data: NSData, encoding: UInt) - inner part utilizes failable initializer, so, NSString? is the result. Then initialization of String with optional NSString? produces optional as well. But, it has no sense at all do it in this way.

First part - remove optional

Utilizing new guard:

guard let loadedPassword = NSString(data: passwordData, encoding: NSUTF8StringEncoding) else {
  fatalError("Ooops")
}

loadedPassword contains NSString (not NSString?) now.

Second part - NSString -> String

You did probably read (if not, read) Strings and Characters about bridging, ... If you can freely exchange NSString with String, you can think that you're done:

var dict = [String:String]()
dict["password"] = loadedPassword

Nope. It produces following error:

NSString is not implicitly convertible to String; did you mean to use 'as' to explicitly convert?

Slight change and now you're done:

var dict = [String:String]()
dict["password"] = loadedPassword as String

Complete example

let password = "Hallo"
guard let passwordData = password.dataUsingEncoding(NSUTF8StringEncoding) else {
  fatalError("Ooops")
}

// save/load to/from keychain

guard let loadedPassword = NSString(data: passwordData, encoding: NSUTF8StringEncoding) else {
  fatalError("Ooops")
}

var dict = [String:String]()
dict["password"] = loadedPassword as String

print(dict) // "[password: Hallo]\n"
zrzka
  • 20,249
  • 5
  • 47
  • 73
  • Hi Robert, Thank you very much, I was just optimizing my code, starting to use guard as well before saving and after loading. This was indeed a overuse of !, no production code at all, was just figuring out where it went wrong. Didn't found that error though at the NSString, – CularBytes Jul 13 '15 at 14:14