How do I check for null when unwrapping optionals from a Swift dictionary? [duplicate] - ios

I have the following code in a playground file:
extension Dictionary {
func test() {
for key in self.keys {
self[key]
}
}
}
var dict: [String: AnyObject?] = [
"test": nil
]
dict.test()
I will henceforth refer to the line within the for-each loop as the output since it is what's relevant. In this particular instance the output is nil.
When I change the for-each loop to look like this:
for key in self.keys {
print(self[key])
}
The output is "Optional(nil)\n".
What I really want to do is check the value for nil, but the code:
for key in self.keys {
self[key] == nil
}
outputs false.
One other thing I tried was the following:
for key in self.keys {
self[key] as! AnyObject? == nil
}
which produces the error:
Could not cast value of type 'Swift.Optional<Swift.AnyObject>' to 'Swift.AnyObject'
Any help with this is much appreciated!

You've gotten yourself into kind a mess, because a dictionary whose values can be nil presents you with the prospect of a double-wrapped Optional, and therefore two kinds of nil. There is the nil that you get if the key is missing, and then the nil that you get if the key is not missing and you unwrap the fetched result. And unfortunately, you're testing in a playground, which is a poor venue for exploring the distinction.
To see what I mean, consider just the following:
var d : [String:Int?] = ["Matt":1]
let val = d["Matt"]
What is the type of val? It's Int?? - an Int wrapped in an Optional wrapped in another Optional. That's because the value inside the dictionary was, by definition, an Int wrapped in an Optional, and then fetching the value by its key wraps that in another Optional.
So now let's go a bit further and do it this way:
var d : [String:Int?] = ["Matt":nil]
let val = d["Matt"]
What is val? Well, the playground may say it is nil, but the truth is more complicated; it's nil wrapped in another Optional. That is easiest to see if you print val, in which case you get, not nil, but "Optional(nil)".
But if we try for something where the key isn't there at all, we get a different answer:
let val2 = d["Alex"]
That really is nil, signifying that the key is missing. And if we print val2, we get "nil". The playground fails to make the distinction (it says nil for both val and val2), but converting to a String (which is what print does) shows the difference.
So part of the problem is this whole double-wrapped Optional thing, and the other part is that the Playground represents a double-wrapped Optional in a very misleading way.
MORAL 1: A dictionary whose value type is an Optional can get really tricky.
MORAL 2: Playgrounds are the work of the devil. Avoid them. Use a real app, and use logging to the console, or the variables pane of the debugger, to see what's really happening.

Checking if a dictionary value is nil makes sense only if the
dictionary values are an Optional type, which means that you have to
restrict your extension method to that case.
This is possible by defining a protocol that all optional types conform
to (compare How can I write a function that will unwrap a generic property in swift assuming it is an optional type?, which is just a slight modification of Creating an extension to filter nils from an Array in Swift):
protocol OptionalType {
typealias Wrapped
var asOptional : Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional : Wrapped? {
return self
}
}
Now you can define an extension method with is restricted to
dictionaries of optional value types:
extension Dictionary where Value : OptionalType {
func test() {
for key in self.keys { ... }
}
}
As Matt already explained, self[key] can only be nil if that key
is missing in the dictionary, and that can not happen here. So you
can always retrieve the value for that key
let value = self[key]!
inside that loop. Better, enumerate over keys and values:
for (key, value) in self { ... }
Now value is the dictionary value (for key), and its type
conforms to OptionalType. Using the asOptional protocol property
you get an optional which can be tested against nil:
if value.asOptional == nil { ... }
or used with optional binding.
Putting all that together:
extension Dictionary where Value : OptionalType {
func test() {
for (key, value) in self {
if let unwrappedValue = value.asOptional {
print("Unwrapped value for '\(key)' is '\(unwrappedValue)'")
} else {
print("Value for '\(key)' is nil")
}
}
}
}
Example:
var dict: [String: AnyObject?] = [
"foo" : "bar",
"test": nil
]
dict.test()
Output:
Value for 'test' is nil
Unwrapped value for 'foo' is 'bar'

Related

Why print() is printing my String as an optional?

I have a dictionary and I want to use some of its values as a key for another dictionary:
let key: String = String(dictionary["anotherKey"])
here, dictionary["anotherKey"] is 42 but when I print key in the debugger I see the following:
(lldb) expression print(key)
Optional(42)
How is that possible? To my understanding, the String() constructor does not return an optional (and the compiler does not complain when I write let key: String instead of let key: String?). Can someone explain what's going on here?
As a sidenote, I am currently solving this using the description() method.
This is by design - it is how Swift's Dictionary is implemented:
Swift’s Dictionary type implements its key-value subscripting as a subscript that takes and returns an optional type. [...] The Dictionary type uses an optional subscript type to model the fact that not every key will have a value, and to give a way to delete a value for a key by assigning a nil value for that key. (link to documentation)
You can unwrap the result in an if let construct to get rid of optional, like this:
if let val = dictionary["anotherKey"] {
... // Here, val is not optional
}
If you are certain that the value is there, for example, because you put it into the dictionary a few steps before, you could force unwrapping with the ! operator as well:
let key: String = String(dictionary["anotherKey"]!)
You are misunderstanding the result. The String initializer does not return an optional. It returns the string representation of an optional. It is an non-optional String with value "Optional(42)".
A Swift dictionary always return an Optional.
dictionary["anotherKey"] gives Optional(42), so String(dictionary["anotherKey"]) gives "Optional(42)" exactly as expected (because the Optional type conforms to StringLiteralConvertible, so you get a String representation of the Optional).
You have to unwrap, with if let for example.
if let key = dictionary["anotherKey"] {
// use `key` here
}
This is when the compiler already knows the type of the dictionary value.
If not, for example if the type is AnyObject, you can use as? String:
if let key = dictionary["anotherKey"] as? String {
// use `key` here
}
or as an Int if the AnyObject is actually an Int:
if let key = dictionary["anotherKey"] as? Int {
// use `key` here
}
or use Int() to convert the string number into an integer:
if let stringKey = dictionary["anotherKey"], intKey = Int(stringKey) {
// use `intKey` here
}
You can also avoid force unwrapping by using default for the case that there is no such key in dictionary
var dictionary = ["anotherkey" : 42]
let key: String =
String(dictionary["anotherkey", default: 0])
print(key)

Cannot assign a value of type 'nil' to a value of type 'NSMutableArray'

I am writing swift code & having a simple problem.
I declared a MSMutableArray, under certain condition, I set it to nil:
func doJob() -> NSMutableArray {
var arr = NSMutableArray()
arr = addContentToArray()
if CRITICAL_CONDITION {
//ERROR: Cannot assign a value of type 'nil' to a value of type 'NSMutableArray'
arr = nil
}
return arr
}
But when I set arr to nil, I got compiler error:
Cannot assign a value of type 'nil' to a value of type 'NSMutableArray'
Why? How can I set it to nil then?
You can assign nil only to optional variables. However, when you are letting the type to be inferred, the compiler doesn't know that you are planning to assign nil in the future:
var arr: NSMutableArray? = NSMutableArray()
However, the whole thing about assigning nil to a variable that has previously held an array seems a bit dirty to me. Maybe it would be easier to use a new variable?
You haven't posted your real code so we can't do a real review but:
if CRITICAL_CONDITION {
arr = nil
}
return arr
can be more easily written as
if CRITICAL_CONDITION {
return nil
}
return arr
That will solve the problem, too, because you won't need to reassign the variable. A different approach would be to use a second variable:
var result: NSArray? = array
if CRITICAL_CONDITION {
result = nil
}
return result
or even better
let result = CRITICAL_CONDITION ? nil: array
return result;
The whole point of specifying when a variable cannot be nil (non-optional) is the fact that optional variables are dangerous and you have to check them for nil all the time. So, use optionals only for a short time, ideally one condition and then convert them to non-optional. In this case, use the non-optional as long as possible and only when you really need to assign nil, convert to optional (declare a second, optional variable).

When should I compare an optional value to nil?

Quite often, you need to write code such as the following:
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
This seems a bit verbose, and also I hear that using the ! force unwrap operator can be unsafe and best avoided. Is there a better way to handle this?
It is almost always unnecessary to check if an optional is not nil. Pretty much the only time you need to do this is if its nil-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil.
Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if for you.
Using the value if it isn’t nil
Instead of:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
you can use if let:
if let i = Int(s) {
print(i + 1)
}
You can also use var:
if var i = Int(s) {
print(++i) // prints 2
}
but note that i will be a local copy - any changes to i will not affect the value inside the original optional.
You can unwrap multiple optionals within a single if let, and later ones can depend on earlier ones:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
You can also add where clauses to the unwrapped values:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
Replacing nil with a default
Instead of:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
or:
let j = i != nil ? i! : 0
you can use the nil-coalescing operator, ??:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
Equating an optional with a non-optional
Instead of:
if i != nil && i! == 2 {
print("i is two and not nil")
}
you can check if optionals are equal to non-optional values:
if i == 2 {
print("i is two and not nil")
}
This also works with comparisons:
if i < 5 { }
nil is always equal to other nils, and is less than any non-nil value.
Be careful! There can be gotchas here:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
Calling a method (or reading a property) on an optional
Instead of:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional chaining, ?.:
let j = i?.successor()
Note, j will also now be optional, to account for the fatalError scenario. Later, you can use one of the other techniques in this answer to handle j’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.
As the name implies, you can chain them, so you can write:
let j = s.toInt()?.successor()?.successor()
Optional chaining also works with subscripts:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
and functions:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
Assigning to a property on an optional
Instead of:
if splitViewController != nil {
splitViewController!.delegate = self
}
you can assign through an optional chain:
splitViewController?.delegate = self
Only if splitViewController is non-nil will the assignment happen.
Using the value if it isn’t nil, or bailing (new in Swift 2.0)
Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil, exit the function early, otherwise keep going.
You might write this like this:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
or to avoid the force unwrap, like this:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").
Instead you can use guard, which is like an if not let:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
The else part must exit the scope of the guarded value, e.g. a return or fatalError, to guarantee that the guarded value will be valid for the remainder of the scope.
guard isn’t limited to function scope. For example the following:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
prints 321.
Looping over non-nil items in a sequence (new in Swift 2.0)
If you have a sequence of optionals, you can use for case let _? to iterate over all the non-optional elements:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}
prints 321. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?.
You can also use this pattern matching in switch statements:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
Looping until a function returns nil
Much like if let, you can also write while let and loop until nil:
while let line = readLine() {
print(line)
}
You can also write while var (similar caveats to if var apply).
where clauses also work here (and terminate the loop, rather than skipping):
while let line = readLine()
where !line.isEmpty {
print(line)
}
Passing an optional into a function that takes a non-optional and returns a result
Instead of:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional’s map operator:
let j = i.map { abs($0) }
This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.
This is nice when you want an optional anyway. For example, reduce1 is like reduce, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard keyword from earlier):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
But instead you could map the .first property, and return that:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals
Sometimes, you want something similar to map, but the function you want to call itself returns an optional. For example:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }
But now idx is of type Int??, a double-optional. Instead, you can use flatMap, which “flattens” the result into a single optional:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
I think you should go back to the Swift programming book and learn what these things are for. ! is used when you are absolutely sure that the optional isn't nil. Since you declared that you are absolutely sure, it crashes if you're wrong. Which is entirely intentional. It is "unsafe and best avoided" in the sense that asserts in your code are "unsafe and best avoided". For example:
if someOptional != nil {
someFunction(someOptional!)
}
The ! is absolutely safe. Unless there is a big blunder in your code, like writing by mistake (I hope you spot the bug)
if someOptional != nil {
someFunction(SomeOptional!)
}
in which case your app may crash, you investigate why it crashes, and you fix the bug - which is exactly what the crash is there for. One goal in Swift is that obviously your app should work correctly, but since Swift cannot enforce this, it enforces that your app either works correctly or crashes if possible, so bugs get removed before the app ships.
You there is one way. It is called Optional Chaining. From documentation:
Optional chaining is a process for querying and calling properties,
methods, and subscripts on an optional that might currently be nil. If
the optional contains a value, the property, method, or subscript call
succeeds; if the optional is nil, the property, method, or subscript
call returns nil. Multiple queries can be chained together, and the
entire chain fails gracefully if any link in the chain is nil.
Here is some example
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
You can check the full article here.
We can use optional binding.
var x:Int?
if let y = x {
// x was not nil, and its value is now stored in y
}
else {
// x was nil
}
After lot of thinking and researching i have came up with the easiest way to unwrap an optional :
Create a new Swift File and name it UnwrapOperator.swift
Paste the following code in the file :
import Foundation
import UIKit
protocol OptionalType { init() }
extension String: OptionalType {}
extension Int: OptionalType {}
extension Int64: OptionalType {}
extension Float: OptionalType {}
extension Double: OptionalType {}
extension CGFloat: OptionalType {}
extension Bool: OptionalType {}
extension UIImage : OptionalType {}
extension IndexPath : OptionalType {}
extension NSNumber : OptionalType {}
extension Date : OptionalType {}
extension UIViewController : OptionalType {}
postfix operator *?
postfix func *?<T: OptionalType>( lhs: T?) -> T {
guard let validLhs = lhs else { return T() }
return validLhs
}
prefix operator /
prefix func /<T: OptionalType>( rhs: T?) -> T {
guard let validRhs = rhs else { return T() }
return validRhs
}
Now the above code has created 2 operator [One prefix and one postfix].
At the time of unwrapping you can use either of these operator before or after the optionals
The explanation is simple, the operators returns the constructor value if they get nil in variable else the contained value inside the variable.
Below is the example of usage :
var a_optional : String? = "abc"
var b_optional : Int? = 123
// before the usage of Operators
print(a_optional) --> Optional("abc")
print(b_optional) --> Optional(123)
// Prefix Operator Usage
print(/a_optional) --> "abc"
print(/b_optional) --> 123
// Postfix Operator Usage
print(a_optional*?) --> "abc"
print(b_optional*?) --> 123
Below is the example when variable contains nil :
var a_optional : String? = nil
var b_optional : Int? = nil
// before the usage of Operators
print(a_optional) --> nil
print(b_optional) --> nil
// Prefix Operator Usage
print(/a_optional) --> ""
print(/b_optional) --> 0
// Postfix Operator Usage
print(a_optional*?) --> ""
print(b_optional*?) --> 0
Now it is your choice which operator you use, both serve the same purpose.

Swift if let evaluates successfully on Optional(nil)

I have a custom object called Field. I basically use it to define a single field in a form.
class Field {
var name: String
var value: Any?
// initializers here...
}
When the user submits the form, I validate each of the Field objects to make sure they contain valid values. Some fields aren't required so I sometimes deliberately set nil to the value property like this:
field.value = nil
This seems to pose a problem when I use an if-let to determine whether a field is nil or not.
if let value = field.value {
// The field has a value, ignore it...
} else {
// Add field.name to the missing fields array. Later, show the
// missing fields in a dialog.
}
I set breakpoints in the above if-else and when field.value has been deliberately set to nil, it goes through the if-let block, not the else. However, for the fields whose field.value I left uninitialized and unassigned, the program goes to the else block.
I tried printing out field.value and value inside the if-let block:
if let value = field.value {
NSLog("field.value: \(field.value), value: \(value)")
}
And this is what I get:
field.value: Optional(nil), value: nil
So I thought that maybe with optionals, it's one thing to be uninitialized and another to have the value of nil. But even adding another if inside the if-let won't make the compiler happy:
if let value = field.value {
if value == nil { // Cannot invoke '==' with an argument list of type '(Any, NilLiteralConvertible)'
}
}
How do I get around this? I just want to check if the field.value is nil.
I believe this is because Any? allows any value and Optional.None is being interpreted as just another value, since Optional is an enum!
AnyObject? should be unable to do this since it only can contain Optional.Some([any class object]), which does not allow for the case Optional.Some(Optional) with the value Optional.None.
This is deeply confusing to even talk about. The point is: try AnyObject? instead of Any? and see if that works.
More to the point, one of Matt's comment mentions that the reason he wants to use Any is for a selection that could be either a field for text input or a field intended to select a Core Data object.
The Swifty thing to do in this case is to use an enum with associated values, basically the same thing as a tagged/discriminated union. Here's how to declare, assign and use such an enum:
enum DataSelection {
case CoreDataObject(NSManagedObject)
case StringField(String)
}
var selection : DataSelection?
selection = .CoreDataObject(someManagedObject)
if let sel = selection { // if there's a selection at all
switch sel {
case .CoreDataObject(let coreDataObj):
// do what you will with coreDataObj
case .StringField(let string):
// do what you will with string
}
}
Using an enum like this, there's no need to worry about which things could be hiding inside that Any?. There are two cases and they are documented. And of course, the selection variable can be an optional without any worries.
There's a tip to replace my Any? type with an enum but I couldn't get this error out of my head. Changing my approach doesn't change the fact that something is wrong with my current one and I had to figure out how I arrived at an Optional(nil) output.
I was able to reproduce the error by writing the following view controller in a new single-view project. Notice the init signature.
import UIKit
class Field {
var name: String = "Name"
var value: Any?
init(_ name: String, _ value: Any) {
self.name = name
self.value = value
}
}
class AppState {
var currentValue: Field?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let f = Field("Amount", AppState().currentValue)
NSLog("\(f.value)")
}
}
In short, I was passing a nil value (AppState().currentValue) to an initializer that accepts Any, and assigns it to a property whose type is Any?. The funny thing here is if I directly passed nil instead, the compiler will complain:
let f = Field("Amount", nil) // Type 'Any' does not conform to protocol 'NilLiteralConvertible'
It seems that somewhere along the way, Swift wraps the nil value of AppState().currentValue in an optional, hence Optional(nil).

Shouldn't an optional be inclusive to a non-optional type in Swift?

Updated: with full code
I have this code:
struct Set<T : Hashable>: Sequence {
var items: Dictionary<Int, T> = [:]
func append(o: T?) {
if let newValue = o {
items[newValue.hashValue] = newValue
}
}
func generate() -> TypeGenerator<T> {
return TypeGenerator ( Slice<T>( items.values ) )
}
}
and I get the error:
Could not find an overload for 'subscript' that accepts the supplied arguments.
for the line:
items[newValue.hashValue] = newValue
As far as I understand it's because newValue's type is T rather than T?, meaning that it's not optional. This is because the Dictionary's subscript for accessing the key/value pair is defined as
subscript (key: KeyType) -> ValueType?
meaning that it can only take an optional as the value. And in my case newValue is not an optional after validating it's not nil.
But isn't an optional inclusive of non-optionals? Isn't a type's optional is the type + nil?
Why would something that can take everything + nil reject a type that can't be nil?
Small clarification: the reason I check that o is not nil is to be able to call its hashValue which is not accessible directly from the optional or the unwrapped optional (o!.hashValue throws a compile error).
I also can't replace the assignment line with
items[newValue.hashValue] = o
because it has validated that o is not an optional worthy of assignment even though it does not allow access to it's hashValue property.
The dictionary is not defined to store an optional value. It is just the assignment operator that accepts an optional value because giving it nil will remove the whole key from the dictionary.
The problem you are having is that you are trying to mutate your property items in a non-mutating method. You need to define your method as mutating:
mutating func append(o: T?) {
if let newValue = o {
items[newValue.hashValue] = newValue
}
}
There is certainly no problem assigning an optional variable to a non-optional value:
var optionalString : String? = "Hello, World"
In the same way, it is perfectly valid to assign a dictionary's key to a non-optional value:
var items : [Int:String] = [:]
items[10] = "Hello, World"
You can then assign the key to nil to remove the key entirely from the dictionary:
items[10] = nil
Also, I think you have a fundamental misunderstanding of what a hashValue is and how to use it. You should not pass the value of hashValue to a dictionary as a key. The dictionary calls hashValue on the value you provide, so you are having the dictionary take the hashValue of the hashValue.
There is no guarantee that a hashValue will be different from all other the hashValues of different values. In other words, the hash value of "A" can be the same hash value as "B". The dictionary is sophisticated enough to handle that situation and still give you the right value for a particular key, but your code is not handling that.
You can indeed store a non-optional value in a Dictionary and in general you can pass an object of type T whenever a T? is expected.
The real problem is that you haven't told the compiler that T is Hashable.
You have to put a type constraint on T, using T: Hashable. You can do this at a class level (I suppose this method lives inside a generic class), like so
class Foo<T: Hashable> {
var items: Dictionary<Int, T> = [:]
func append(o: T?) {
if let newValue = o {
items[newValue.hashValue] = newValue
}
}
}

Resources