cannot return String in function - swift

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!

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"
}

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

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())

Related

Assertion Failure on getting a key from NSDictionary in Swift

I'd like to understand how could the function below sometimes generates a Swift "_assertionFailure" in line:
if let s = dict![key] as? String
I suppose that if dict![key] is not found, a nil would return, so the if let would get a nil value and the condition would fail, with no errors, no assertions. Where I am getting wrong?
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
var value: String?;
if (dict != nil && !key.isEmpty)
{
if let s = dict![key] as? String {
value = s;
}
}
return value;
}
Your syntax is very, very objective-c-ish.
In Swift you can simply write
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
return dict?[key] as? String
}
It contains all checks except the empty string check. The question mark after dictaborts the chain if the dictionary is nil.
You should not use NSDictionary in Swift but a more meaningful naming
func getValue(from dict: [String:Any]?, forKey key: String) -> String?
{
return dict?[key] as? String
}

How do I return safely unwrapped optionals which are outside of their scope?

I'm a noob, bear with me:
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString)
}
return (emailString+employeeCodeString) //ERROR: Use of unresolved identifier 'employeeCodeString' & Use of unresolved identifier 'emailString'
}
I understand the reason the error shows is because I'm trying to return something that is in a different scope here, but how else can I get the function to return the 2 strings together without the "Optional[...]" tag?
Here's how I'd expect it to be done in a normal production app
(You wouldn't do any of this in a normal production app! But this is the "idiom" you're looking for.)
func createCodeIfPossible() -> String? {
guard let e = UserDefaults.standard.object(forKey: "email_Saved") else {
print("serious problem, there's no email saved")
// at this point the app is completely buggered, so give up
return ""
}
guard let c = UserDefaults.standard.object(forKey: "employeeCode_Saved") else {
print("serious problem, there's no code saved")
// at this point the app is completely buggered, so give up
return ""
}
return e + c
}
Do note that the return is largely meaningless - in the app in question, if one of the guards breaks you are "totally screwed". I'd probably just return a blank string (or more likely something like "name_error") since at this point the app architecture is hopelessly broken.
(Sidenote: use UserDefaults.standard.string(forKey:).)
The issue is that you can't know if those strings DO both exist or not--if they do, you already have a great if let that returns your answer. The question now is what do you want to do if one or both are nil? Maybe you'd like to return nil from the entire function. If so,
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString) //successful unwrapping, let's concatenate!
}
return nil //if one or both `if let`s fail, we end up here
}
Of course, you could do whatever you'd like in that "bad" case. Maybe you'd like to show whatever string you DO have. In that case:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
return (email ?? "") + (employeeCode ?? "") //this works in the "good" case, too, and uses the nil coalescing operator `??`
}
In this case, you can see that the return value is no longer optional. This is because even if neither string exists, it'll concatenate two empty strings. If this feels icky, you could keep your optional return value and do a quick check before returning:
if email == nil && employeeCode == nil { return nil }
func createEmployeeCode() -> String {
var finalString = String()
if let email = UserDefaults.standard.string(forKey: "email_Saved"), let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved") {
finalString = email + employeeCode
}
return finalString
}
When assign back the values from userDefaults you've been trying to get as an object instead of string
func createEmployeeCode() -> String? {
let email:String = UserDefaults.standard.string(forKey: "email_Saved") ?? ""
let employeeCode:String = UserDefaults.standard.string(forKey: "employeeCode_Saved") ?? ""
let emailString = "\(email)\(employeeCode)"
return emialString
}
There are different ways to solve this depending on what you're trying to achieve.
If you always want to create an employeeCode (even if the code will be empty):
Try using a "nil coalescing operator".
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String ?? ""
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String ?? ""
return (email+employeeCode)
}
To explain what's happening here:
We're unwrapping email, if we don't find email then we default the value to an empty string, "".
We do the same with employeeCode.
This isn't a way I would solve every unwrap issue though, but it suits your usecase of email and employeeCode because you're always wanting to return something based on your original question. I've also changed the return type to non-optional.
If an employee code must always contain an email and and a code then we want to return nil if one of those isn't found.
Try using the guard statement. The guard statement is perfect for validation and very readable:
func createEmployeeCode() -> String? {
guard let email = UserDefaults.standard.object(forKey: "email_Saved") as? String else { return nil }
guard let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String else { return nil }
return (email+employeeCode)
}
Try this function:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
return [email, employeeCode].compactMap { $0 }.joined(separator: "")
}
It will return email or employeeCode or email+employeeCode in case one of them is nil or both are present, or empty String in case if both are missed out!

swift 4 model class error: Return from initializer without initializing all stored properties

I created a model class for notes that have the attributes title and content. I am getting an error in the second init that says: Return from initializer without initializing all stored properties. Im feel like there is something I am missing and I cant seem to see it. This is the code:
class Note{
private var _title: String
private var _content: String
var title: String!{
return self._title
}
var content: String!{
return self._content
}
init(title: String, content: String){
self._title = title
self._content = content
}
init(noteData: Dictionary<String, AnyObject>) {
if let title = noteData["title"] as? String {
self._title = title
}
if let content = noteData["content"] as? String {
self._content = content
}
}
}
You have to initialise all properties of your class in its initializer. Regarding your dictionary initializer you should make it fallible and return nil in case of missing key/value pairs:
class Note {
let title: String
let content: String
init(title: String, content: String) {
self.title = title
self.content = content
}
init?(dictionary: [String: Any]) {
guard
let title = dictionary["title"] as? String,
let content = dictionary["content"] as? String
else { return nil }
self.title = title
self.content = content
}
}
Note that if use a struct instead of a class you don't even have to implement an initializer for your custom object:
struct Note {
let title: String
let content: String
init?(dictionary: [String: Any]) {
guard
let title = dictionary["title"] as? String,
let content = dictionary["content"] as? String
else { return nil }
self.title = title
self.content = content
}
}
The problem is that in the second init, if the unwrap fails the properties will not be set. For the init to work correctly the properties that are not optional will all need values. The best way to fix this is to make the properties optional.
Optional properties:
private var title: String?
private var content: String?
By making the properties optional you can avoid doing the if let in the init:
self.title = noteDate["title"]
Make sure to unwrap safely when you use the properties though!

Remove Force Unwrapping

Remove force unwrapping
private func getLanguageCode() -> String {
return Locale.current.languageCode!
}
After removing force unwrapping
private func getLanguageCode() -> String? {
if let language = Locale.current.languageCode {
return language
}
return nil
}
Correct??
for this
lazy var commonErrorList : Dictionary<String, AnyObject>? = {
let path = Bundle.main.path(forResource: "CommonErrorCodes", ofType: "plist")
return NSDictionary(contentsOfFile: path!) as? [String : AnyObject]
}()
what it should be after removing forced unwrapping?
You could just
return Locale.current.languageCode
without the forced unwrap, if you just return nil anyway.
You may try guard to prevent nested if statement in the future. In guard statement it would be something like this:
private func getLanguageCode() throws -> String {
guard let language = Locale.current.languageCode else {
throw LanguageError.CanNotGetCode
}
return language
}
or without throws
private func getLanguageCode()-> String? {
guard let language = Locale.current.languageCode else {
return nil
}
return language
}
You may use throwing function if in the future you want to handle your function error.
Your code looks fine to me. You can consider also using guard statement.
private func getLanguageCode() -> String? {
guard let language = Locale.current.languageCode else { return nil }
return language
}
If you want to return a non-optional String, you can just do:
private func getLanguageCode() -> String {
return Locale.current.languageCode ?? ""
}
If you prefer to return a String?, then:
private func getLanguageCode() -> String? {
return Locale.current.languageCode
}
If you still want to detect the nil condition inside your function for whatever reason:
private func getLanguageCode() -> String? {
guard let language = Locale.current.languageCode else {
// Do something here...
return nil
}
return language
}
If you don't want to deal with an unwrap, you can easily do this:
private func getLanguageCode() -> String {
return Locale.current.languageCode ?? "en"
}
This will return the value of the optional if available, otherwise it will return the specified value.
You can also use a property to get the expected value:
private var getLanguageCode: String {
return Locale.current.languageCode ?? "en"
}
The final result is pretty the same, but you get rid of the '( )' notation :-)

Convenience initializer with non-optional property

An object of mine has an integer ID. Since this is a required property I am not defining it as an optional and I am requiring it in the designated initializer:
class Thing {
var uniqueID: Int
var name: String?
init (uniqueID: Int) {
self.uniqueID = uniqueID
}
}
Since I am creating one of these from some JSON, the usage is along the lines of:
if let uniqueID = dictionary["id"] as? Int {
let thing = Thing(uniqueID: unique)
}
Next, I would like to be able to add a convenience initializer to the Thing class that accepts the dictionary object and sets the properties accordingly. This includes the required uniqueID and some other optional properties. My best effort so far is:
convenience init (dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
//set other values here?
}
//or here?
}
But of course this isn't sufficient since the designated initializer isn't called on all paths of the conditional.
How should I be handling this scenario? Is it even possible? Or should I accept that uniqueID must be an optional?
You have a couple of options with this one. One is a failable initialisers:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
Technically this can be tweaked a bit (mainly depending on your preference/version of swift), but my person preference is something as follows:
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
All together, as a playground:
class Thing {
var uniqueID: Int
var name: String?
init(uniqueID: Int) {
self.uniqueID = uniqueID
}
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
}
let firstThing = Thing(uniqueID: 1)
let secondThing = Thing(dictionary: ["id": 2])
let thirdThing = Thing(dictionary: ["not_id": 3])
let forthThing = Thing.fromDictionary(["id": 4])
let fithThing = Thing.fromDictionary(["not_id": 4])
The best solution is probably to use a failable initializer, which will either return an instantiated object or nil.
Because Swift objects cannot be partially constructed and convenience initializers must call a non-convenience initializer, we must still do something in the failure case.
The result will look something like this:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: 0)
return nil
}
}
Generally speaking, our non-convenience initializer(s) should be one that accepts all arguments, and convenience initializers should be methods which don't require some of the arguments.
For example, I might make my default initializer look like this:
init(uniqueID: Int, name: String? = nil) {
self.uniqueID = uniqueID
self.name = name
}
This allows us to call the constructor in several different ways:
let thing1 = Thing(1)
let thing2 = Thing(2, nil)
let thing3 = Thing(3, "foo")
let thing4 = Thing(4, myUnwrappedStringVar)
let thing5 = Thing(5, myWrappedStringOptional)
And that already covers a lot of use cases for us.
So, let's add another convenience initializer that accepts an optional Int.
convenience init?(uniqueID: Int? = nil, name: String? = nil) {
if let id = uniqueID {
self.init(uniqueID: id, name: name)
} else {
self.init(uniqueID: 0)
return nil
}
}
Now we can take an Int? for our uniqueID argument and just fail when it's nil.
So, one more to accept the dictionary.
convenience init?(dictionary: [String: AnyObject]) {
let uniqueID = dictionary["id"] as? Int
let name = dictionary["name"] as? String
self.init(uniqueID: uniqueID, name: name)
}
We still have the slightly weird initialize then return nil pattern in our first convenience constructor, but everything else we build on top of this can simply call that convenience initializer and doesn't require the weird pattern.
In the initializer that takes the dictionary, if there's no id key, or if it's something that's not an Int, then the let uniqueID will be nil, so when we call the other constructor, it will call the one that accepts an Int?, be passed nil, return nil, and therefore the one we called will return nil.