0

In the code below I create a function called startTimer. The purpose of this function is to count down from an input received by @IBAction. I am trying to understand:

Why do I need to declare a var secondsRemaining? Why do I need to use a self keyword to be able to refer to the

Full Code

import UIKit
    
    class ViewController: UIViewController {
        
        
    let eggTimes : [String : Int] = ["Soft": 300, "Medium": 420, "Hard": 720]
        var secondsRemaining: Int? // self.secondsRemaining
    @IBAction func hardnessSelected(_ sender: UIButton) {
        let hardness = sender.currentTitle!
        let timerSeconds = eggTimes[hardness]!
        startTimer(secondsRemaining: timerSeconds)
        //until here the code seems to work fine

        func startTimer (secondsRemaining: Int?){
        //create a function called startTimer which accepts an interger as argument called secondsremaining
            self.secondsRemaining = secondsRemaining
            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
                if self.secondsRemaining! > 0 {
                    //if the secondsRemaining >
                    print ("\(self.secondsRemaining ?? 0) seconds")
                    self.secondsRemaining! -= 1
                }else {
                    Timer.invalidate()
                  }
                }
        }
        //call the function start timer and give the secondRemaining argument the value of timerSeconds
       

    }
    

Previous to this my function was like this

 func startTimer (secondsRemaining: Int?){
        //create a function called startTimer which accepts an interger as argument called secondsremaining
           // self.secondsRemaining = secondsRemaining
            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
                if secondsRemaining! > 0 {
                    //if the secondsRemaining >
                    print ("\(secondsRemaining ?? 0) seconds")
                    secondsRemaining! -= 1
                }else {
                    Timer.invalidate()
                  }
                }
        }

Which would return me the error :

Left side of mutating operator isn't mutable: 'secondsRemaining' is a 'let' constant

This makes me ask two questions:

  1. Are all the arguments declared for functions always constants? If so, why
  2. If the answer to question 1 is No, how can I declare an argument as a variable in a function?
djumanji
  • 43
  • 5
  • A parameter to a function is by default immutable, if you need to change it you need to declare it as `inout`, `func startTimer (secondsRemaining: inout Int)` – Joakim Danielson Jun 06 '21 at 05:53

2 Answers2

1

Are all the arguments declared for functions always constants?

No, there are inout parameters which you can change in the function body. There were also var parameters, but they got removed in Swift 3.

The purpose of inout parameters is for the function to be able to change the argument passed in, and have the changes reflect on the caller's side. An example from the Swift guide:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    // you are able to change a and b here
    a = b
    b = temporaryA
}

var a = 1
var b = 2
swapTwoInts(&a, &b)
// now b is 1 and a is 2

This means that you must pass something mutable to inout parameters, not something like timerSeconds which is a let constant.

Furthermore, you can't use an inout in an escaping closure, like the one you pass to Timer. See my explanation at the start of this answer. This might also be helpful.

If you don't like to use a class-level, you can simply declare a local var:

func startTimer (totalTime: Int?){ // I renamed the parameter
    var secondsRemaining = totalTime
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
        ...
Sweeper
  • 213,210
  • 22
  • 193
  • 313
1

All parameters passed into a Swift function are constants, so you can’t change them. If you want, you can pass in one or more parameters as inout, which means they can be changed inside your function, and those changes reflect in the original value outside the function.

func startTimer (secondsRemaining: inout Int){
   //your code here
}

To use that, you first need to make a variable integer – you can’t use constant integers with inout, because they might be changed. You also need to pass the parameter to doubleInPlace using an ampersand, &, before its name, which is an explicit recognition that you’re aware it is being used as inout.

startTimer(secondsRemaining: &timerSeconds)