Swift 1.2 Concatenate String - swift

I'm getting errors when concatenating string:
let likeKey = "like-" + foodPhotoObjects[indexPath.row].objectId
Error
binary operator '+' cannot be applied to operands of type 'String' and 'String?!'

So, you have an implicitly-wrapped optional of an optional string, something like this:
struct Thing {
let objectId: String?!
}
let foodPhotoObjects: [Thing] = [Thing(objectId: "2")]
With any doubly-wrapped optional, to get to the object inside you’d need to unwrap it twice:
// first unwrap the String?! into a String?
if let outer = foodPhotoObjects[0].objectId,
// then unwrap that String? into a String
inner = outer {
// inner is now a String
println("like-\(inner)")
}
The key here is even though the outer optional is implicit (i.e. ! rather than ?), you can still unwrap implicit optionals using if let, so the implicitness is irrelevant when doing this.
An alternative way of handling this kind of thing, rather than if-let, is to use map:
let concatedString = foodPhotoObjects[indexPath.row].objectId.map {
"like-" + $0
} ?? ""
map on an optional means: if the optional contains a value, change the value using this function and return that as an optional, otherwise return nil. So, unwrap the String? and prepend “like” to it.
?? on an optional means: if the preceding value is nil, replace it with the default on the right-hand side (the empty string), otherwise unwrap it and return that (i.e. the value we just mapped).
Now for the tricky part: because the value we’re calling map on is an implicit optional, it will be implicitly unwrapped – that is, the map is being called on the inner String? rather than on the String?!. This is unlike the case with if let where that was run on the implicit optional first, then the inner optional.
As with all implicit optionals, there’s a risk that they might actually be nil in which case your code would blow up, like so:
let explode = Thing(objectId: nil)
// the next line will generate fatal error: unexpectedly
// found nil while unwrapping an Optional value
explode.objectId.map { "like-" + $0 }
If this is a concern, you could guard against it with some optional chaining:
// note, ? after objectId
let concatedString = foodPhotoObjects[indexPath.row].objectId?.map {
"like-" + $0
} ?? ""
This snippet could win a prize for most optional-handling techniques crammed into a single statement… but it should do what you need.

Swift does not do implicit conversion, even if both are of same type and one of them is of optional type.
Try this.
var concatedString = ""
if let foodphoto = foodPhotoObjects[indexPath.row].objectId as? String {
concatedString = "like-" + foodphoto
}

Related

Why don't you need to unwrap an optional type value when being set?

Does it mean that the set operation doesn't read the actual value of the Optional, so it doesn't need to be unwrapped?
var str = "Hello, playground"
class Base{
var name:String?
};
var obj = Base()
obj.name = "hello" //this line don't need unwrapping first
An optional is a box. The box can either contain nothing (which is called nil) or it can contain something of a specific type (a String in your example). You unwrap an optional to access the value inside in the box.
When you are assigning a value to an optional, you just need to assign the value to the box itself. There is no need to unwrap anything, because you are just putting a value into the box. Swift either empties the box, if you assign nil, or it wraps the value by putting it into the box.
Unwrapping is for accessing a value that is already in the box.
Answering your question from a comment on another answer...
But why Optional binding don't need unwrapping? i think i if let constantName = some Optional is kind of assignment too
Optional binding is an unwrapping operation and an assignment operation. It says "if there is a value inside the box, assign it to this new variable and enter the then clause, otherwise proceed to the else clause if it exists".
var optionalValue: String? = "hello"
if let value = optionalValue {
// value is the contents of the box, it has type String
print("value is \(value)")
} else {
// the optional binding failed because the box is empty
print("optionalValue is nil")
}
When you set an Optional property, you do not have to unwrap it.
Unwrapping is required only when assigning an optional value to a non-optional property:
var name: String = "" // non-optional
var str: String?
// this will not compile, because you need to unwrap str first
name = str
// this will compile, because we're providing a default value
name = str ?? ""
// this will also compile, because name is not an optional
// it would still compile if name was optional, because str is optional too
str = name

Swift: '!=' cannot be applied to operands of type 'String' and 'Any'

I am trying to check if the string is empty of not, if it is not empty, i want to display the at the textField (outUrl.text)
let storage = UserDefaults.standard
var mainsite = storage.object(forKey:"sitestring") ?? ""
if (mainsite != "") {
outUrl.text = mainsite
}
But I have error:
Error: != cannot be applied to operands of type string and any
How do i check if the string is not empty?
The recommended solution is to use the appropriate API (string(forKey:) which returns String rather than Any and optional bindings.
The benefit is you get rid of any type cast and the nil coalescing operator
if let mainsite = UserDefaults.standard.string(forKey:"sitestring"), !mainsite.isEmpty {
outUrl.text = mainsite
}
The result returned by the Objective-C API -[UserDefaults obejectForKey:] is of type id, in order to accomodate various types of objects (NSString, NSNumber, etc, etc.). When accessed form Swift code, this becomes Any? (any type, perhaps nil).
If you know the stored value is indeed a string, you need to attempt cast it before comparing: != between values of types String and Any? isn't defined by default! What meaning would that have?):
var mainsite = storage.object(forKey:"sitestring") as? String ?? ""
This way, you either get a String value (if the cast succeeds) or the empty string (if it fails, because either there is no object for key "sitestring" or it isn't convertible to a String value).
The result from UserDefaults.standard is of type Any?, so before assigning the value to mainsite variable, typecast it to String. Here is an example,
let storage = UserDefaults.standard
var mainsite = storage.object(forKey:"sitestring") as? String

Type cast optional values

Whenever we need to du something with an optional value in swift we need to unwrap it to operate, not on the optional, but on the value ”inside” the optional.
var optionalString:String? = "Hello"
optionalString!.append(" World!")
Note the exclamation mark on the second line.
But when using the optional type cast operator (as?) on an optional value, one does not need to unwrap the optional, we just provide it with the optional itself, and it just magically works.
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString as? String
Note the absent exclamation mark on the second line.
The magic is a bit more obvious if we spell it out:
let sneakyString:Any? = Optional("Hello")
let notSoSneakyString = sneakyString as? String
Its not a string we're trying to cast but an enum with a string as an associated value.
I would expect that I would have to do this:
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString! as? String
Note the exclamation mark on the second line.
Does the type cast operators act on optionals and non optionals in the same way?
The as? operator makes a cast if it possibly can, and returns either the casted object or nil. It handles nil values too, so that if sneakyString were nil, you wouldn't have been able to cast it to a String and the cast would have failed. Same behaviour as if it were non-nil but not castable to String.
In other words, you don't need the ! because as? handles nil values automatically itself.

most concise way of unwrapping and casting optional

I have a bit of code to get a string out of userDefaults:
if let userString = (userDefaults.objectForKey("user")) {
userTextField.stringValue = userString as! String
}
First, I have to see if the optional is not nil. Then I have to cast it as a string from AnyObject.
Is there a better way of doing this? maybe a one liner?
Note that your forced cast as! String will crash if a default value for the key "user" exists, but
is not a string. Generally, you can combine optional binding (if let) with an optional cast (as?):
if let userString = userDefaults.objectForKey("user") as? String {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
But actually NSUserDefaults has a dedicated method for that purpose:
if let userString = userDefaults.stringForKey("user") {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
If you want to assign a default string in the case that
the default does not exist, or is not a string, then use the
nil-coalescing operator ??, as demonstrated in
Swift issue with nil found while unwrapping an Optional value NSDefautlts, e.g.:
userTextField.stringValue = userDefaults.stringForKey("user") ?? "(Unknown)"
For the special case NSUserDefaults the best – and recommended – way is to use always non-optional values.
First register the key / value pair in AppDelegate as soon as possible but at least before using it.
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValues = ["user" : ""]
defaults.registerDefaults(defaultValues)
The benefit is you have a reliable default value of an empty string until a new value is saved the first time. In most String cases an empty string can be treated as no value and can be easily checked with the .isEmpty property
Now write just
userTextField.stringValue = userDefaults.stringForKey("user")!
Without arbitrary manipulation of the defaults property list file the value is guaranteed to be never nil and can be safely unwrapped, and when using stringForKey there is no need for type casting.
Another way that i like much to clean this up is to do each of your checks
first, and exit if any aren’t met. This allows easy understanding of what
conditions will make this function exit.
Swift has a very interesting guard statements which can also be used to avoid force unwrap crashes like :
guard let userString = userDefaults.objectForKey("user") as? String else {
// userString var will accessible outside the guard scope
return
}
userTextField.stringValue = userString
Using guards you are checking for bad cases early, making your
function more readable and easier to maintain. If the condition is not
met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically
unwrapped for you within the scope that the guard statement was
called.

Chaining Optionals in Swift

Up until now, I've been unwrapping Optionals in Swift 2.1 like so:
#IBOutlet var commentTextView: UITextView!
if let comment = user["comment"] as? String {
commentTextView.text = comment
}
I never really thought about it, but I think the reason I was doing this was because I was worried that this statement would throw an error if user["comment"] returned something other than a String:
commentTextView.text = user["comment"] as? String
If user["comment"] isn't a String, will the variable on the left of the assignment operator be assigned and throw an error or will the assignment be skipped?
I guess user is in fact a dictionary [String: Any] and what you really do with if let comment = user["comment"] as? String { ... } is not just unwrapping the optional but a conditional type casting (and then unwrapping an optional result of it):
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
Now, to answer your question, if user["comment"] isn't a String then the result will be that commentTextView.text will be assigned nil value, which is bad because its type is String! (implicitly unwrapped optional) about which we hold a promise that it will never be nil. So, yes, there will be an error, an exception actually, but not at the place you would like it to be but at the moment your application will try to access its value assuming that it's not going to be nil.
What you should really do depends on a particular case.
E.g. if you can make user to be a dictionary like [String: String], then you would be able to truly get to unwrapping the optionals and use something like if let comment = user["comment"] { ... }. Or, if you are totally sure that the value for "comment" key will always be there, then you could just do let comment = user["comment"]!.
But if that's not possible then you have to stick with down-casting and the only other thing you can do is to use forced form of it, that is commentTextView.text = user["comment"] as! String. This one at least will produce an exception right at the spot in case if the value at "comment" happens to be not a String but something else.
nil will be assigned to the variable.
If the type of the variable is a non-optional, you'll get a runtime error.
However if user["comment"] is a String you'll get a compiler error about missing ! or ?.
First we need to know of what type the dictionary "user" is.
I assume it is of an unknown type like [String: AnyObject], otherwise why would you try to unwrap it as an String. Let us write a short test to see what happens:
let dict: [String: AnyObject] = ["SomeKey" : 1]
if let y = dict["SomeKey"] as? String {
print(y)
}
You can see clearly that the value of "SomeKey" is an Integer. Trying to unwrap it as an String triggers no error, the "if" statement is just skipped. If an assignment actually happened is hard to prove (maybe by looking at the assembler code) because the variable "y" simply does not exist after the if statement. I assume it will not be created at all.
If the type of the dictionary is known as [String: String] you can omit the try to unwrap it as a String because it's always clear that the type is String.
let dict2: [String: String] = ["SomeKey" : "SomeValue"]
if let y = dict2["WrongKey"] {
// In this case print(y) will not be called because the subscript operator of the dictionary returns nil
print(y)
}
// In this case print(y) will be called because the key is correct
if let y = dict2["SomeKey"] {
print(y)
}