I'm new to Swift and I'm wondering if there's a 'short hand' form of assigning Optional values to non-optional values without doing if nil check.
For example if I've got an Optional closure:
var onTap: (() -> Void)?
I can later on call this via
onTap?()
The ? let's me skip the nil check and force unwrap.
But I'm curious if something exists for values that are not clousre. For example
var oldTransform: CGAffineTransform?
var someTransform: CGAffineTransform //non optional
and later on, I decide to assign oldTransform to someTransform I have to
if (oldTransform != nil) {
someTransform = oldTransform!
}
Is there a pattern that's less verbose to do the above (kinda of like the ?)?
Yes, of course. You could use if let as below:
if let unwrappedOldTransForm = oldTransform {
someTransform = unwrappedOldTransForm
}
When you check with if let, it will unwrap the value for you and check if it can set the value of oldTransform to unwrappedOldTransForm. If the value is nil you will be able to handle the error by adding an else condition as below:
if let unwrappedOldTransForm = oldTransform {
someTransform = unwrappedOldTransForm
} else {
// handle if oldTransform is nil
}
In the case of a function, as you know, "calling" a nil function (like onTap?()) has no effect - but for an assignment to a non-optional type you need to supply a definite non-optional value. For that you can use the nil-coalescing operator ?? like so:
var optionalString: String?
// ... maybe set optionalString ...
var realString: String = optionalString ?? "<some default value>"
In your example, it would be
someTransform = oldTransform ?? CGAffineTransform(...) // some default - say Identity
or even, as commentator #user28434 suggests
someTransform = oldTransform ?? someTransform // assuming someTransform already initialised
which will only change someTranform if oldTransform isn't nil.
In each case, the left hand operand of ?? will be assigned if is non-nil, otherwise the right hand side operand will be assigned.
Related
swift code is not working after clean build.
how can i rewrite this simple code?
if let name: String = String(cString: (interface?.ifa_name)!), name == "utun0" {
print("Ipfound")
ipFound = true;
}
i put image for better understanding.
The if let construct is used for "conditional binding" of an optional. The thing you are trying to assign to name (the function result) is not optional - instead, the value you are passing to String(cString:,name:) is optional.
You should rewrite your code to something like this:
if let interfaceName = interface?.ifa_name {
let name = String(cString: interfaceName)
if name == "utun0" {
print("Ipfound")
ipFound = true
} else {
print("name != Ipfound")
}
} else {
print("interface or interface.ifa_name is nil")
}
In that code I'm using optional binding to try to get a non-nil value from interface?.ifa_name.
If you use ! "force unwrap" operator as you were trying to do, your code would crash if interface is nil, or if interface.ifa_name is nil.
Edit:
Another way you could handle this, that would look closer to your original code, would be to use map() on the Optional:
if let name: String = interface?.ifa_name.map ({ String(cString: $0)}),
name == "utun0" }
{
print("Ipfound")
ipFound = true;
}
The form of map() that works on an Optional takes an Optional as input. If that optional is nil, it returns nil. If the optional is not nil, the result returned by map() is the result of applying your closure to the unwrapped optional.
I need to unwrap the optional and use it as a parameter to the function. If the optional is nil, then I need to default to a specific value.
Is there a shorter/cleaner way to code this example?
func iTake(aNonOptional nonOptionalInt: Int) -> Float {
// do something with nonOptionalInt
return Float(nonOptionalInt) * 0.125
}
var anOptionalInt: Int?
let aSpecificDefaultReturnValue: Float = 0.99
let result: Float
if let aNonOptionalInt = anOptionalInt {
result = iTake(aNonOptional: aNonOptionalInt)
} else {
result = aSpecificDefaultReturnValue
}
My actual use-case is more complicated, but this is the idea.
The function can't be rewritten, but can the if let be written in a better way?
Thanks in advance.
var anOptionalInt: Int?
let aSpecificDefaultReturnValue: Float = 0.99
let result = anOptionalInt.map { iTake(aNonOptional: $0) } ?? aSpecificDefaultReturnValue
You can use the fact that the Swift Optional enum has a map function, and couple it with the nil coalescing operator:
Optional.map looks inside your optional 'box' and (if it finds something in the box) uses the transform you supply to alter your value, or ignores the transform if you have nothing inside the box (i.e. a nil).
The nil coalescing operator ?? basically says 'return everything left of me if that thing is not-nil, otherwise return everything right of me'.
You could also do it like that:
let result: Float = (anOptionalInt != nil) ? iTake(aNonOptional: anOptionalInt!) : aSpecificDefaultReturnValue
If anOptionalInt is not nil, the iTake function is called taking as argument the unwrapped value of the anOptionalInt variable (force unwrapping here is ok, since we've checked already whether it's nil or not). If it's nil, then keep the default value as the result.
Dears
I have this case where chatId is a property of type Int
let StringMessage = String(self.listingChat?.messages.last?.chatId)
When I debug I find that StringMessage is returning Optional(15) Which means it is unwrapped. But at the same time XCode does not allow me to put any bangs (!) to unwrap it. So I am stuck with Unwrapped Variable. I know its noob question but it I really cant get it. Your help is appreciated.
Thank you
It depends on what you want the default value to be.
Assuming you want the default value to be an empty string (""), You could create a function or a method to handle it.
func stringFromChatId(chatId: Int?) -> String {
if let chatId = chatId {
return String(chatId)
} else {
return ""
}
}
let stringMessage = stringFromChatId(self.listingChat?.messages.last?.chatId)
Or you could handle it with a closure.
let stringMessage = { $0 != nil ? String($0!) : "" }(self.listingChat?.messages.last?.chatId)
If you don't mind crashing if self.listingChat?.messages.last?.chatId is nil, then you should be able to directly unwrap it.
let StringMessage = String((self.listingChat?.messages.last?.chatId)!)
or with a closure
let stringMessage = { String($0!) }(self.listingChat?.messages.last?.chatId)
Update
Assuming chatId is an Int and not an Optional<Int> (AKA Int?) I missed the most obvious unwrap answer. Sorry, I was tired last night.
let StringMessage = String(self.listingChat!.messages.last!.chatId)
Force unwrap all the optionals along the way.
Optionals have a very nice method called map (unrelated to map for Arrays) which returns nil if the variable is nil, otherwise it calls a function on the (non-nil) value. Combined with a guard-let, you get very concise code. (I've changed the case of stringMessage because variables should begin with a lower-case letter.)
guard let stringMessage = self.listingChat?.messages.last?.chatId.map { String($0) } else {
// Do failure
}
// Success. stringMessage is of type String, not String?
I think:
let StringMessage = String(self.listingChat?.messages.last?.chatId)!
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
}
I have a variable
var a: [AnyObject? -> Void]
and I am adding data in to it by append method. Now I want to check if the variable is nil or not. I tried using [] but not working and also tried "", this also not working, can anyone tell what is the meaning of this variable and how to check if it is nil.
As far as I understand, var a is an Array of functions that take an optional Object of any type, and return void. So these functions's parameter IS optional, but the Array itself isn't : it cannot be nil, or it would be declared [AnyObject? -> Void]? , no?
EDIT : if, nevertheless, you declared this a as an optional (but WHY would you do that ?) - adding a ? - you check an optional existence with if let :
if let b = a {
// a not nil, do some stuff
} else {
// a is null
}
If you just want to check if the array is empty, use isEmpty method from Swift Array
Update: Xcode 7.3 Swift 2.2
If you want to check if a variable is nil you should use if let to unwrap if for you. There is no need to create a second var.
let str = "123"
var a = Int(str)
if let a = a {
print(a)
}
Or
if let a = Int(str) {
print(a)
}
In Swift, nil is not a pointer—it is the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.
So, You can check it with below code:
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
if convertedNumber != nil {
println("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."
Please refer this about nil for more information.
In Swift 3.0
if let imageURL = dictObj["list_image"] as? String {
print(imageURL)
}
You can use if let. if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value.
var a:Int=0
if let b=a{
println(a)
} else {
println("Value - nil")
}
But for Strings you can also use .isEmpty() If you have initialized it to "".
var str:String=""
if !str.isEmpty(){
println(str)
}
For me none of the above solutions worked when I was using an AVFoundation object.
I would get Type 'AVCaptureDeviceInput does not conform to protocol 'BooleanType' when I tried if (audioDeviceInput) and I would get Binary operator '!=' cannot be applied to operands of type 'AVCaptureDeviceInput' and 'nil'.
Solution in my situation
if (audioDeviceInput.isEqual(nil))
nil is a pointer like any other and can be referenced as such, which is why this works.