3

What's the difference between these two declarations? Which one is better? Why?

... error = some NSError ...

1.

var newUserInfo: [NSObject: NSObject] = [:]

if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
  newUserInfo = tempUserInfo
}

2.

var newUserInfo: [NSObject: NSObject]

if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
  newUserInfo = tempUserInfo
} else {
  newUserInfo = [:]
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Daniel Gomez Rico
  • 15,026
  • 20
  • 92
  • 162

3 Answers3

7

As of Swift 1.2, you can now use let with deferred assignment so you can use your if/else version:

let newUserInfo: [NSObject: NSObject]

if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
  newUserInfo = tempUserInfo
} else {
  newUserInfo = [:]
}

However, option 1 will not work, since there is a path where newUserInfo may not be set.

(note, as of 1.2b1, this doesn't work with global variables, only member and local variables, in case you try this out in a playground)

Alternatively, you could use the nil-coalescing operator to do it in one go, like this:

let newUserInfo = (error.userInfo as? [NSObject:NSObject]) ?? [:]

edit: Swift 1.2 added deferred assignment of let, enabling option 2 to be used with let now, but also changed the precedence of as? vs ??, requiring parens.

Pre-1.2 answer in case you have similar code you need to migrate:

Neither are particularly appealing if you ask me. In both cases, you have to have to declare newUserInfo with var, because you're not declaring and assigning it in one go.

I'd suggest:

let newUserInfo = error.userInfo as? [NSObject:NSObject] ?? [:]
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Just found it http://mjtsai.com/blog/2014/07/25/nil-coalescing-operator-in-swift/ – Daniel Gomez Rico Dec 17 '14 at 14:54
  • 1
    Indeed - looks like the precedence of `as?` vs `??` has changed. Brackets fixes – `let newUserInfo = (error.userInfo as? [NSObject:NSObject]) ?? [:]`, however there's also the new option of using `let` with the `if/else`, I'll update my answer. – Airspeed Velocity Feb 11 '15 at 13:00
2
  • In 1. newUserInfo is assigned twice if the if branch is executed. 2. is better in terms of performance
  • In 1. it's clearly visible that newUserInfo is initialized as an empty array. 2. makes the code less readable because you have to browse the code to know if it has a default value
  • If newUserInfo can be set in several places (such as if it can be initialized it in several if statements), you should duplicate the else branch in 2., so 1. looks better

So: in solution no. 1 code is more readable, but solution no. 2 is slightly more performant.

Besides using @AirspeedVelocity solution (which is better than yours, no offence :)), I'd rather prefer to use an optional, setting newUserInfo to nil to indicate absence of value - after all, that's what optionals are for, isn't it? But of course that depends on your specific needs.

Antonio
  • 71,651
  • 11
  • 148
  • 165
0

My preferred pattern for this is:

struct TryUnrap<T> {
    typealias Tryee = () -> T?
    typealias Catchee = () -> T

    private var tryee: Tryee

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func catch(catchee: Catchee) -> T {
        if let result = tryee() {
            return result
        }
        return catchee()
    }
}

let error: NSError? = NSError()
let newUserInfo = TryUnrap {
    error?.userInfo as? [NSObject : NSObject]
}.catch {
    [:]
}
println("newUserInfo = \(newUserInfo)")

I prefer the above because I find it reads better for me. Also see answer 4 of Error-Handling in Swift-Language for general error handling using the same pattern.

Community
  • 1
  • 1
Howard Lovatt
  • 968
  • 1
  • 8
  • 15
  • 3
    This is essentially a non-idiomatic inefficient implementation of `??` for recovering Java programmers. – Airspeed Velocity Dec 17 '14 at 01:14
  • For me it looks more unreadable than the others, I prefer the first answers... but it's personal. – Daniel Gomez Rico Dec 17 '14 at 14:49
  • I don't find ?? very readable and it can't be used in other try catch situations either. However it is a matter of personal taste. The efficiency is almost certainly inconsequential, you are initialising a let which by definition is a one off. – Howard Lovatt Dec 19 '14 at 04:31