Search code examples
swiftstructparametersviewmodel

swift how to update viewModel parameter while init


I have encountered the problem "Cannot assign to property: 'self' is an immutable capture". How can i call API to check status and update "getUpdateSuccess" parameter

struct HomepageViewModel {

  var getUpdateSuccess: Bool = false

  init() {

       getStatusUpdated.execute().done {
            [self] isUpdated in

            // Cannot assign to property: 'self' is an immutable capture
            self.getUpdateSuccess = isUpdated
        }

   }
}


Solution

  • If you want to “reference” self later (e.g., update the same instance later), self should be a reference type, not a value type.

    So you can solve this by making the view model a class (a reference type) rather than a struct (a value type).

    “Model” objects are good candidates for value types, but “view models” are generally reference types. See Value and Reference Types for a discussion of when you might use a value type and when you might use a reference type. For a general discussion, see The Swift Programming Language: Structures and Classes.


    By the way, the closure’s capture list syntax (the stuff in between the [ and the ]) has different practical implications if the captured variable is a reference type vs when it is a value type.

    With a value type, the capture list makes sure you have a copy of the value:

    struct Person {…}
    
    var person = Person(…)
    foo { [person] in 
        // this code will be dealing with a safe copy of the original `Person`
    }
    
    // maybe you’ll mutate the original `Person` instance here,
    // but the above closure will have a copy of the original one
    

    Contrast that with a reference type, where the capture list makes sure you have a strong reference to the reference type:

    class ViewModel {
        func bar() {
            baz { [self] in
                // this has a strong reference to this view model instance, not a copy of the instance
            }
        }
    }
    
    let viewModel = ViewModel(…)
    viewModel.bar()   // any updates that bar does asynchronously, will update this instance of the view model, not a copy of it
    

    As an aside, with reference types, you will often see a capture list with the weak keyword, e.g., [weak self], if you want a “weak” reference to the class rather than a strong reference. This is common if you need to prevent a “strong reference cycle” (a topic beyond the scope of this conversation). This is discussed in The Swift Programming Language: Strong Reference Cycles for Closures.

    But, whether a strong reference or weak reference, you are not dealing with a copy of the object, but rather a “reference” to the same original instance.