2

I'm struggling with a for-in-loop in Swift. I have two for loops and I expected them be equivalent but the first gives an error and the second works as I expected. Can someone explain me why it's working differently?

protocol Slide {
    var title: String { get set }
}

class BasicSlide: NSObject, Slide {
    var title: String = "Title"
}

var slides: [Slide]?
slides = [BasicSlide()]

for slide in slides! {
    slide.title = "New title" // Cannot assign to property: 'slide' is a 'let' constant
}

for var i in 0 ..< slides!.count {
    slides![i].title = "New title"
}
Lodewijck
  • 364
  • 6
  • 19
  • 2
    Making the protocol to conform `class` will fix the `for-in` problem: `protocol Slide: class { }`. – Eendje Jan 29 '16 at 15:30
  • Okay, so it's because slide could also be an Enum implementing the Slide protocol, and in that case this wouldn't work? – Lodewijck Jan 29 '16 at 15:47
  • @Eendje there are several solutions to the problem in the question, but your solution is interesting, you know why this happen? I guess making the protocol class-only maybe had the properties in the `for-in` by default `var`, is only a guess, in the doc the use of `class` in protocols is very poor. – Victor Sigler Jan 29 '16 at 16:08

1 Answers1

5

As you rightly say, slide is a constant in this expression:

for slide in slides! {
    slide.title = "New title" // Cannot assign to property: 'slide' is a 'let' constant
}

If you need to mutate your slide, simply take a var reference to it:

for slide in slides! {
    var slide = slide
    slide.title = "New title" // fine
}

(Do not use the for var syntax, as it will soon be abolished from the language.)

However, in your example there's a much better way: declare your protocol like this:

protocol Slide : class {
    var title: String { get set }
}

Now this code is legal!

for slide in slides! {
    slide.title = "New title"
}

Why? Because you've now guaranteed that the adopter of Slide is a class, and a class is a reference type and can be mutated in place. The only reason this failed before is that Swift was afraid the adopter might be a struct.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Note: as of Swift 5.9 in 2023, the "for var" syntax is alive and well. It was not removed from the language, apparently. – Bryan Jun 11 '23 at 05:30