Tryswift hector matos cover?fm=jpg&fl=progressive&q=75&w=300

Hipster Swift

At a high level, Swift is amazing and brings so much to us developers to use on a daily basis. However, there are many little-known things in Swift that can save us time and energy if we memorize a few key features. This talk from try! Swift will cover many features that look weird and translates them for beginners and experts alike to understand. Armed with this information, developers should hopefully be able to save lots of time decrypting the weird things they see on a day-to-day basis!


Introduction (0:00)

This post will be about Hipster Swift. Before delving into what I mean by that, here’s an overview of what will be covered:

  • @noscape
  • @autoclosure
  • lazier
  • vars
  • variadic parameters
  • labeled loops
  • type omitting

@noescape (1:56)


NSNotificationCenter.defaultCenter().addObserverForName("PokemonAttackListener",
    object: nil,
    queue: queue,
    usingBlock: { notification in
    //Determine if the attack is super effective!
})

@noescape was always a very weird thing to look at. As a preface, remember that in Swift, closures are first class citizens. They can be assigned to a variable, so they can be torn down in memory just like any class instance. Because of this, many of us forget that some of the closures we pass into functions can be stored off somewhere for later use.

Take for example the notification center’s block-based API. The closure we pass into the usingBlock argument of this function actually gets stored off into a separate array of blocks mapped to a dictionary that uses the notification name that you see up there as the key. This means that we are giving notification center the responsibility of deallocating our block for us. After the addObserver function got called, the closure essentially escaped our addObserverForName function, meaning it did not get called before the end of the function scope.

Get more development news like this

If we want to know that the closures we pass in are called immediately, we use @noescape.

func trainForTheFightAgainstFrieza(@noescape preparations: () -> ()) {
  //The prep closure MUST be called in the body of this function
  //or in a function immediately called from here.
  preparations()
}

@noescape is a Swift attribute that you can use to annotate a function’s closure parameter. What we are essentially telling the caller of the trainForTheFightAgainstFrieza function is that the closure will be called before the end of the function’s control flow.

LOL nice try (4:42)

func trainForTheFightAgainstFrieza(@noescape preparations: () -> ()) {
  escapedClosure = preparations
  tryToEscapeClosure(preparations)
  tryToEscapeClosure() {
    preparations()  
  }
}

func tryToEscapeClosure(closure: () -> ()) {
  escapedClosure = closure
}

Even though I’ve tried my absolute best to make the preparations closure that you see there escape the control flow of the trainForTheFightAgainstFrieza function, the compiler wouldn’t have any of it. It gives you errors when you try to make a non-escaping closure escape. One really cool thing about no escape is that it gives you extra optimizations behind the scenes at runtime. It’s important to note that, you do not have to capture self inside of a non-escaping closure.

@autoclosure (5:48)


func assert(condition: @autoclosure () -> Bool, message: String) {
  if !condition() {
    fatalError(message)
  }
}

let zFighters = ["Goku", "Vegeta", "Gohan", "Trunks"]
assert(zFighters.contains("Krillin"), message: "Looks like Krillin isn't a Z-Fighter. That sucks.")

In the function here, we’re doing a very simple test to see if our noble Z-Fighters array contains our beloved character Krillin. Instead of passing a closure to the assert function, we’re actually just passing a boolean. This looks weird for us because the assert function accepts the closure, but when we call it, we’re not actually passing in a closure. @autoclosure automatically wraps your argument in a closure. It creates a non-escaping closure for you.


func assert(condition: @noescape () -> Bool, message: String) {
  if !condition() {
    fatalError(message)
  }
}

let zFighters = ["Goku", "Vegeta", "Gohan", "Trunks"]
assert({
    return zFighters.contains("Krillin")
  }, message: "Looks like Krillin isn't a Z-Fighter. That sucks." )

@noescape just snuck back in there. This is because auto closures automatically apply the @noescape attribute to your closure parameter as well. In fact, the code that you see above is exactly equivalent to this code. You can see why we would want to use @autoclosure.

Inline Lazy Variables (8:08)


lazy var kamehameha: KiAttack = {
  //Charging a Kamehameha Wave takes a long time.
  //This is why we want this variable to be lazy.
  //We don't want to have to charge one for five episodes every time
  //we have to save the world.
  return self.chargeAndReleaseAKamehameha()
}

A drawback to lazy variables is that they look like a huge function as opposed to actual variables. This is where inline lazy variables come into play, and it looks like this.

lazy var kamehameha: KiAttack = self.chargeAndReleaseAKamehameha()

I always have to explicitly use unowned self in my lazy variables. Most of that is done behind the scenes. Inline lazy variables do something very similar to @autoclosure in that they wrap the right hand side of the = operator in a closure for you. However, it does capture self strongly by default. When you use this, remember it will capture self strongly. But beware of reference cycles.

For the sake of showing an example that actually works without creating a reference cycle, here you go:


class Goku: ZFighter {
  lazy var kamehameha: KiAttack = self.chargeAndReleaseAKamehameha()

  func chargeAndReleaseAKamehameha() -> KiAttack {
    return KiAttack()
  }

  deinit {
    print("deinit is called")
  }
}

var goku: Goku? = Goku()
goku?.kamehameha.attackEnemy()
goku = nil

It shows that our deinit actually gets called after we set goku to nil because there is no strong reference cycle here. So for the most part, you can use this very safely. Just make sure you pay attention when you use this to avoid reference cycles.

Labeled Loops (11:20)


func winnerOfBattleBetween(enemy: Enemy, andHero hero: Hero) -> Fighter? {
  var winner: Fighter?
  for enemyAttack in enemy.listOfAttacks {
    var heroWon = false
    for heroAttack in hero.listOfAttacks {
      if heroAttack.power > enemyAttack.power && completedEpisodes.count > 5 {
        heroWon = true
        winner = hero
        break
      }
    }
    if heroWon {
      break
    }
  }
  return winner
}

print(winnerOfBattleBetween(majinBuu, andHero: Goku)) //prints Goku

This is a very long function that figures out the winner of our epic battle between Majin Buu and Goku. You may notice the stupid boolean logic that we have in there just to figure out when to break out of both loops. Swift has labeled loops to assist in writing the code so that it’s easier to read.

func winnerOfBattleBetween(enemy: Enemy, andHero hero: Hero) -> Fighter? {
  var winner: Fighter?
  enemyFightLoop: for enemyAttack in enemy.listOfAttacks {
    heroFightLoop: for heroAttack in hero.listOfAttacks {
      if heroAttack.power > enemyAttack.power && completedEpisodes.count > 5 {
        winner = hero
        break enemyFightLoop
      }
    }
  }
  return winner
}

print(winnerOfBattleBetween(majinBuu, andHero: Goku)) //prints Goku

No more booleans. I’ve actually been able to apply a label to both loops. In the first loop, I’ve applied the enemyFightLoop label with a colon and a space, and then the regular for loop. And the inner for loop, I’ve applied the heroFightLoop label.

Right right below the winner = hero line is break enemyFightLoop. Because you are explicitly telling the program to break the outer loop without having to break the inner loop first, then figure out whether or not you should break the outer loop, and we get rid of booleans. It is a lot more readable. I’ve used it for a break statement, but we’re not just limited to that - we can also use it for continue statements as well.

Type Omission (14:16)


func changeSaiyanHairColorTo(color: UIColor) {
  saiyan.hairColor = color
}

There are essentially three hair colors in Dragonball Z. If a Saiyan turns into a Super Saiyan, his hair turns yellow. If it isn’t, then it’s black. Trunks is the only different character in the show, and he has purple hair. Three hair colors.

If we were to write logic to use this function, it would probably look like this.


if saiyan.isSuperSaiyan {
  changeSaiyanHairColorTo(UIColor.yellowColor())

} else {
  if saiyan.name == "Trunks" {
    changeSaiyanHairColorTo(UIColor.purpleColor())
  } else {
    changeSaiyanHairColorTo(UIColor.blackColor())
  }
}

If you’re Super Saiyan, change it to yellow. For all other cases, unless you’re Trunks, you’re black. This is pretty, it works, and you could even say it’s Swifty. I’d argue, though, that it’s not Swifty until you use type omission. By using type omission, we can actually take advantage of type imprints and use our beloved enum sugar syntax on static variables and functions.


if saiyan.isSuperSaiyan {
  changeSaiyanHairColorTo(.yellowColor())

} else {
  if saiyan.name == "Trunks" {
    changeSaiyanHairColorTo(.purpleColor())
  } else {
    changeSaiyanHairColorTo(.blackColor())
  }
}

There’s also one also very important thing to remember to make that work: our static variable or function has to be inside of a class or value type, and it has to return an instance of that class or value type. Let’s see how this looks like when we create our own type omission capable function.

struct PowerLevel {
  let value: Int
  static func determinePowerLevel(_ fighter: ZFighter) -> PowerLevel
}

func powerLevelIsOver9000(powerLevel: PowerLevel) -> Bool {
  return powerLevel.value > 9000
}

// Type omission!
if powerLevelIsOver9000(.determinePowerLevel(goku)) {
  print("It's over 9000!")
}

Here’s a determinePowerLevel function that is a static function of a PowerLevel struct, and it returns an instance of . We also have a helper method called powerLevelIsOver that accepts an instance of PowerLevel. Since our static determinePowerLevel function returns an instance of PowerLevel, and it’s a member of the PowerLevel struct, we can use type omission and completely omit the need to type out PowerLevel every single time we want to use it.

Normally you would use powerLevel.determinePowerLevel and pass in Goku when you want to use that bottom if statement. However, because we pass all the rules for a static function that is a member of a class or value type that returns that class or value type, we can use the determine power level function just like you would use an enum. Since an enum’s case is essentially a static variable inside of an enum and it returns an instance of that enum, this is why we can use that dot sugar syntax when we work with enums. This is no different than when we do the same thing for our static function in there.

Q&A

Q: Is there a difference between @autoclosure and inline lazy variables?

Hector: Yes, there is. Inline lazy variables behave like auto closure but they aren’t auto closure. So that being said, they do capture self strongly by default but the answer to your question is they are different.

Q: When you have an @autoclosure, does that mean when you call it, you can still write a full closure if that’s more appropriate for what you’re doing?

Hector: Yes.

Q: Is there any situation where you would advise not to put @autoclosure on there?

Hector: Almost every situation. In fact, Apple actually advises against using it liberally throughout your code, because the unfortunate part about it is it reduces readability.

About the content

This talk was delivered live in March 2017 at try! Swift Tokyo. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Hector Matos

Raised by llamas in the great state of Texas, Hector grew to be an avid couch potato who likes spending his precious couch time playing The Legend of Zelda or yelling at the TV whilst watching Game of Thrones, and his other time with his lovely daughter and wife. When not vegging at home or blogging about Swift, you can find him sitting at the office writing mobile apps for iOS & Android for Capital One. With a particular penchant for great mobile UI/UX, Hector writes the code that makes the world go round.

4 design patterns for a RESTless mobile integration »

close