Search code examples
swiftinout

Initialize instance variables inside instance function


final class TestVC: UIViewController {
    var usersFooter: Footer!
    var groupsFooter: Footer!

    override func viewDidLoad() {
        super.viewDidLoad()

        bind(footer: &usersFooter)
    }

    func bind(footer: inout Footer) {

        footer = Footer(style: .autoFooter, height: 57) {
            // doing something
        }
    }
}

Thats what Footer is:

final class Footer: RefreshView {
    private var loader: MDCActivityIndicator!

    override public init(style: Style, height: CGFloat, action: @escaping () -> Void) {
        // initializing and doing something with loader

        super.init(style: style, height: height, action: action)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

I get this:

Cannot pass immutable value of type 'Footer' as inout argument

How to pass TestVC instance's footers in it's function and be able to initialize them? Why is footer that immutable (Declared as var)?


Solution

  • This occurs because

    var someVar: Footer!
    

    does not define a variable of type Footer but a variable of type Optional<Footer> that is implicitly unwrapped. The code:

    var someFooter: Footer! 
    
    bind(footer: &someFooter) 
    

    is logically equivalent to

    var someFooter: Footer? 
    
    guard let tempFooter = someFooter? else { fatalError() }
    bind(footer: &tempFooter) 
    

    As you can see, tempFooter is a let, so it can't be passed as an inout variable and even if it could, the result would be thrown away.

    You can fix this in one of three ways:

    • make the parameter to bind an optional, e.g. func bind(footer: inout Footer?) or use Martin's syntax to make it implicitly optional.

    • Force the unwrap yourself:

      var unwrapped: Footer = someFooter
      bind(footer: unwrapped)
      someFooter = unwrapped
      
    • redesign the API. It seems the first thing you do in the bind function is overwrite the old footer with a newly initialised footer. So don't use an inout parameter, return the value you want i.e.

      func bind() -> Footer
      {
          var theFooter = Footer(...) { ... }
          // do stuff
      
          return theFooter
      }
      
      someFooter = bind()
      

    I think the last option is the best in this case.