Returning an Unwrapped Optional in Swift? - xcode

When I was sifting through some of the class discussions for AVFoundation, I stumbled upon the following:
class func defaultDeviceWithMediaType(mediaType: String!) -> AVCaptureDevice!
Because optionals are a new concept to me, I'm a bit confused.
The discussion says that this method could either return "the default device with the given media type, or nil if no device with that media type exists." However, if there is a possibility that it's returning nil, why do they unwrap this optional in the return statement? Shouldn't it be AVCaptureDevice?
Then, when looking at an example that utilizes the above method, I find the following:
public lazy var device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
public func hasFlash() -> Bool {
if let d = self.device {
return d.hasFlash
}
return false
}
From what I understand, you would use an if let statement when you have an optional, but because the class defaultDeviceWithMediaType returns an unwrapped variable, why is having an if let necessary?
Thank you so much in advance.

Implicitly unwrapped optional is basically an optional, that gets an ! everywhere you use it. Thats it.
e.g.:
//this:
var number: Int? = ...
print(number!)
//is the same as this:
var number: Int! = ...
print(number)
An implicitly unwrapped optional is only to save you the need of unwrapping it every time you use it, wether with if let or with an !, but it has the same optionality of being nil as a normal optional.
A popular use of Implicitly unwrapped optionals is with outlets - they can't be non-optionals because we don't init them in the init of the VC, but we definitely have them later, so having them unwrapped saves us the need to do annoying things like if let table = self.tableView....

An implicitly unwrapped optional is still an optional - it could be nil. If you simply write self.device.hasFlash, when self.device is nil, you will get an exception.

Related

Swift Wrap and Unwrap

I'm a little confused about Swift Wrap and Unwrap! So lets say this is my code:
var name:String? = "FirstName"
print(name)
Does the print function automatically unwrap the name which is optional? so I do not need to say
print(name!) in order to unwrap the name?
I guess what I am trying to understand is these two are equivalent for unwraping an optional variable?
print("name") is just like saying print ("name"!)
The other question I have is about nil.
is saying var name:String? = "FirstName" equivalent to saying var name:String? = nil . So does assigning a nil value wraps a variable?
When something can be nil it can be two things, it can be Some (the value of the given type) or it can be nil.
declaring something like this:
var name: String?
Means that the name variable can be nil, if you assigned a value to it you need to unwrap it to use it.
name = "FirstName"
Now the name variable has been defined, however you still need to ensure it's not nil in some cases, in other cases however (such as when the string doesn't need to be not nil) optional chaining is used.
Optional chaining allows the continuous evaluation of nil or some throughout a statement as long as it's not required to be not nil. If that is the case then you will need to unwrap it:
let someThingRequiresAString = NeedAStringInitializer(string: name!)
In the above statement if name is nil the program will crash, there are several approaches to dealing with things like this, here's a quick example:
if name != nil {
let someThingRequiresAString = NeedAStringInitializers(string: name!)
}
Here you know you can do this b/c name has been evaluated to not be nil. You can also use a nil coalescing operator, or a guard statement. Here's a quick example of nil coalescence in Swift:
let someThingRequiresAString = NeedAStringInit(string: name ?? "New Name")
The optional paradigm is quite powerful and expressive.

Swift optionals - warning on conditional cast from 'x' to 'x' always succeeds

I was wondering if there is a way to turn off/avoid 'yellow' warnings in xcode on if let...NSUserDefaults constructs where the key is of a known value.
For example:
if let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey") as? Int {...}
Because of the if let I have to use as?. However, as I am using a known value type (in this case integer) the as? Int is effectively redundant - which is why I am getting the 'yellow warning'.
Thoughts? Is there a better way to code these types of constructs?
My suggestion would be to address the issue instead of silencing the warnings. :)
NSUserDefaults.standardUserDefaults().integerForKey("myKey") does not return an Optional, and the type is known, so you don't need neither optional binding with if let nor type casting with as?.
Just this:
let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey")
will suffice, since .integerForKey just returns 0 if it can't get the actual value.
If you don't like this behavior of getting a default value (I don't), then don't use .integerForKey and use objectForKey with optional binding and type casting instead. Like you were doing first but with .objectForKey replacing .integerForKey. That way you'll get an actual nil if the value for the key is unreachable, not a default value.
if let x = NSUserDefaults.standardUserDefaults(). objectForKey("myKey") as? Int {...}
First of all check always the signature:
⌥-click on the symbol integerForKey: or look at Quick Help.
You will see:
func integerForKey(_ defaultName: String) -> Int
It reveals the return value is a non optional.
Non optionals can retrieved directly as described in Eric's answer without any type casting, optional binding causes an error.
That's one of the essential semantics in Swift.

Swift: Having a "for in loop" where the range is determined by a return statement

I was building a custom UI and I realize that for some reason I cannot do this.
protocol notImportant{
SegementButtons(segmentControl : VerticalSegmentControl) -> Int
}
//trying to use the function later in this fashion below
for index in 0...delegate?.segementButtonsCount(self)
Now I know there are many other solutions.
First of all, is this valid or must I provide a concrete number or variable?
Continued
Xcode shows an error
Binary operator '...' cannt be applied to oraands of type Int and Int?
I type cast the return value to an Int, changing the error to
Type Int does not conform to protocol SequenceType
Now it would be pretty cool if I could make this work without Xcode cutting itself.
delegate is an optional, therefore the type of the expression
delegate?.segmentButtonsCount(self)
is also an optional (which is nil if delegate == nil).
You can use optional binding to unwrap the delegate
if let theDelegate = delegate {
for index in 0 ..< theDelegate.segmentButtonsCount(self) {
// do something ...
}
}
or use the nil-coalescing operator ?? to provide a
default value:
for index in 0 ..< (delegate?.segmentButtonsCount(self) ?? 0) {
// do something ...
}
Note that since array indices are zero-based, you probably want to use the range operator ..< which
excludes the end element.

Swift double unwrapping of Optionals

I understand what optional are in Swift but I just encountered a ”Double Wrapped Optional’, where if I don’t use two '!' Xcode gives an complier error
Value of optional type 'String?' not unwrapped; did you mean to use '!' or ‘?'?
I have the following code, where app is of type NSRunningApplication.
let name: String = app.localizedName!
Why should I have to use two !? Isn’t one enough to unwrap the variable because it is of type var localizedName: String?.
Context:
Xcode want me to use let name: String = app.localizedName!!, otherwise it gives the compiler error above.
The app variable is defined as follow:
var apps = NSWorkspace().runningApplications.filter{$0.activationPolicy == NSApplicationActivationPolicy.Regular}
for app in apps{
//code posted above
…
}
So I know that app is not an optional and will always have a value, nor is it an optional application.
P.S. Is there a way to define type when using fast enumeration? Like for Foo(app) in apps where apps = [AnyObject].
The problem is that NSWorkspace().runningApplications returns an
array of AnyObject which has to be cast to an array of
NSRunningApplication:
let apps = NSWorkspace().runningApplications as! [NSRunningApplication]
let filteredApps = apps.filter {
$0.activationPolicy == NSApplicationActivationPolicy.Regular
}
for app in apps {
let name: String = app.localizedName!
}
Here's why: app is of type AnyObject (id in Objective-C), and doing any lookup on AnyObject introduces a layer of optionality because of the possibility that the method doesn’t exist on the object. localizedName is itself Optional, so you end up with two levels of optional: the outer level is nil if the object doesn’t respond to localizedName, and the inner is nil if 'localizedName' is nil.

Is Swift type-inference contradicting itself here?

Here's my test code:
var myDict: [String: AnyObject] = ["k":"v"]
var a = myDict["k"]
var b = a as String
var c = myDict["k"] as String
Here's my Swift playground in Xcode6-beta6:
According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?
I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:
var myDict: [String: Any] = ["k":"v"]
then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.
Note that if you change the last line to:
var c = myDict["k"] as NSString
the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.
Update:
So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:
So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.
This has to do with the fact that Dictionary has two subscript overloads:
subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }
The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.
The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.
When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.
This works.
var c = myDict["k"] as AnyObject! as String // "v"
To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:
var c = myDict["k"] as AnyObject? as String // "v"
Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.

Resources