Avoid Consecutive "If Let" Declarations in Swift

Avoid consecutive if let declarations in Swift

I wrote a little essay on the alternatives some time ago: https://gist.github.com/pyrtsa/77978129090f6114e9fb

One approach not yet mentioned in the other answers, which I kinda like, is to add a bunch of overloaded every functions:

func every<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)): return .Some((a, b))
default: return .None
}
}

func every<A, B, C>(a: A?, b: B?, c: C?) -> (A, B, C)? {
switch (a, b, c) {
case let (.Some(a), .Some(b), .Some(c)): return .Some((a, b, c))
default: return .None
}
}

// and so on...

These can be used in if let statements, case expressions, as well as optional.map(...) chains:

// 1.
var foo: Foo?
if let (name, phone) = every(parsedName, parsedPhone) {
foo = ...
}

// 2.
switch every(parsedName, parsedPhone) {
case let (name, phone): foo = ...
default: foo = nil
}

// 3.
foo = every(parsedName, parsedPhone).map{name, phone in ...}

Having to add the overloads for every is boilerplate'y but only has to be done in a library once. Similarly, with the Applicative Functor approach (i.e. using the <^> and <*> operators), you'd need to create the curried functions somehow, which causes a bit of boilerplate somewhere too.

Using multiple let-as within a if-statement in Swift

Update for Swift 3:

The following will work in Swift 3:

if let latitudeDouble = latitude as? Double, let longitudeDouble = longitude as? Double {
// latitudeDouble and longitudeDouble are non-optional in here
}

Just be sure to remember that if one of the attempted optional bindings fail, the code inside the if-let block won't be executed.

Note: the clauses don't all have to be 'let' clauses, you can have any series of boolean checks separated by commas.

For example:

if let latitudeDouble = latitude as? Double, importantThing == true {
// latitudeDouble is non-optional in here and importantThing is true
}

Swift 1.2:

Apple may have read your question, because your hoped-for code compiles properly in Swift 1.2 (in beta today):

if let latitudeDouble = latitude as? Double, longitudeDouble = longitude as? Double {
// do stuff here
}

Swift 1.1 and earlier:

Here's the good news - you can totally do this. A switch statement on a tuple of your two values can use pattern-matching to cast both of them to Double at the same time:

var latitude: Any! = imageDictionary["latitude"]
var longitude: Any! = imageDictionary["longitude"]

switch (latitude, longitude) {
case let (lat as Double, long as Double):
println("lat: \(lat), long: \(long)")
default:
println("Couldn't understand latitude or longitude as Double")
}

Update: This version of the code now works properly.

How to avoid multiple if let statements in Swift 2

There are several solutions:

  • guard
  • multiple let/var inside the same if
  • params with default values
  • ternary conditional operator
  • Nil Coalescing Operator
  • Don't accept optional params in the init (as suggested by nhgrif)

Looking at your example it seems that

  • self.title
  • self.initMainText
  • self.initBtnTitle

are optionals so you could simply write:

init(title: String?, mainText: String?, buttonTitle: String?) {
self.title = title
self.initMainText = mainText
self.initBtnTitle = buttonTitle
...
}

Using if let... with many expressions

Update for Swift 1.2

Since Swift 1.2, if let allows unwrapping multiple optionals, so you can now just write this, as in your example:

if let x = someDict[someKey], y = someDict[someOtherKey] { … }

You can even interleave conditions such as:

if let x = someDict[someKey] where x == "value", y = someDict[someOtherKey] { … }

This used to be valid before Swift 1.2

Here's how you would do it without an ugly force-upwrapping:

switch (dict["a"], dict["b"]) {
case let (.Some(a), .Some(b)):
println("match")
default:
println("no match")
}

Still pretty verbose, actually.

This works because an optional type of the form Type? is actually shorthand for Optional<Type>, which is an enum that looks roughly like this:

enum Optional<T> {
case None
case Some(T)
}

You can then use pattern matching as for any other enum.

Edit: I've seen people write helper functions like this one (sorry for the lack of attribution, I don't remember where I saw it):

func unwrap<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)):
return (a, b)
default:
return nil
}
}

Then you can keep using the if let construct, namely like this:

if let (a, b) = unwrap(dict["a"], dict["b"]) {
println("match: \(a), \(b)")
} else {
println("no match")
}

Multiple if let and OR condition in swift

if let value1 = profile.value1, value1 != “” {}
else if value2 = profile.value2, value2 != “” {}
else if value3 = profile.value3, value3 != “” {}

here , acts like &&

Pay attention:
For Swift 2 replace , with where

Why do I get consecutive declarations on a line must be separated by and variable used within its own initial variable

Executable code, like if let urlPath = ..., cannot go at the top level of a class declaration like you have it. It must go inside a function declaration.



Related Topics



Leave a reply



Submit