Josh Moore February 2016

cannot return String in function

I am having trouble casting an option AnyObject into a string. Whenever I try to call the fuction my program crashes with (lldb). This is the function.

func name() -> String {
    print(attributes["name"])
    print(attributes["name"]! as! String)

    let name  = attributes["name"]! as! String
    return name
}

The output from the prints is:

Optional(Optional(Josh))
Josh
(lldb) 

Thanks in advance for your help!

Answers


Kyle February 2016

if let _string = attributes["name"] as? String {
    return _string
}
// fallback to something else, or make the method signature String?
return ""

When working with optionals, you don't want to just wrap things with exclamation points. If the value ever ended up not being a string, or not being there at all in the map, you're code would fail hard and potentially crash your application.

If you need a non-optional String, consider returning an empty string as a fallback method and using the if let pattern to return the optional string if it is available.

-- EDIT --

Not sure about the downvote... Here it is in a playground.

var attributes = [String:AnyObject]()
attributes["name"] = "test"

func name() -> String {
    print(attributes["name"])
    print(attributes["name"]! as! String)

    let name  = attributes["name"]! as! String
    return name
}

// does not compile
//print(name())

func name2() -> String {
    if let _string = attributes["name"] as? String {
        return _string
    }
    // fallback to something else, or make the method signature String?
    return ""
}

// prints test
print(name2())


dzk February 2016

In all these examples, attributes is defined as:

var attributes: AnyObject? = ["name": "Josh"]

Looks like the crash occurs due to type-safety issues. Try:

func name() -> String? {
    if let name = attributes!["name"] as? String {
        return name
    }
    return nil
}

Another option, which is slightly swiftier:

func name() -> String? {
    guard let name = attributes!["name"] as? String  else { return nil }
    return name
}

Yet another option that would be using a block for the function, so that it doesn't return anything if attributes doesn't contain a key "name":

func name(block: ((text: String?) -> Void)) {
    guard let name = attributes!["name"] as? String  else { return }
    return block(text: name)
}

// Usage:
name { text in
    print(text!)
}

Prints:

Josh


appzYourLife February 2016

Lets say attributes is defined as follow

var attributes: NSMutableDictionary? = NSMutableDictionary()

and can be populated like follow

attributes?.setValue("Walter White", forKey: "name")

Optionals

You should design the name() function to return a String or nil (aka String? which is an Optional type)

func name() -> String? {
    guard let
        attributes = attributes,
        name = attributes["name"] as? String else { return nil }
    return name
}

The same logic can also be written this way

func name() -> String? {
    return attributes?["name"] as? String
}

Now if a valid String value is found inside attributes with key name then it is returned. Otherwise the function does return nil.

Invoking the function

When using the function you should unwrap the result like this

if let name = name() {
    print(name) // prints "Walter White"
}

Post Status

Asked in February 2016
Viewed 3,334 times
Voted 5
Answered 3 times

Search




Leave an answer