0

I'm retrieving data from a website.

Networking works well. Data is parsed correctly from JSON.

A couple of references - In this struct:

  • Replies is the datamodel for the JSON
  • PrepareQuestions is a func which performs the parsing (I have it in an extension of the same Struct)

I'd like to have an object within this struct (downloadedData - 'Replies' is the struct with the datamodel) containing all the information downloaded, but I incur into an error due to "self being an immutable capture". Any suggestions? Thank you!

    struct QuestionsManager {
    
    var downloadedData:Replies?
    
    func useData() {
        manageQuestions(url: K.urlForRetreival, numberOfQuestions: K.numberOfSquares) { [self] (replies, error) in
            if let replies = replies {
                DispatchQueue.main.async {
                    downloadedData = replies // Here I got the error
                }
            }
        }
    }
    
    func manageQuestions(url: String, numberOfQuestions: String, myCompletion: @escaping (Replies?, Error?)->()) {
        let generatedUrl = URL(string: url + numberOfQuestions)!
        let urlSession = URLSession(configuration: .default)
        let task = urlSession.dataTask(with: generatedUrl) { (data, response, error) in
            if error == nil {
                if let fetchedData = data {
                    let fetchedProcessedData = prepareQuestions(data: fetchedData)
                    myCompletion(fetchedProcessedData, nil)
                    return
                }
            } else {
                myCompletion(nil, error)
                return
            }
        }
        task.resume()
    }
}
MRoot
  • 45
  • 6

2 Answers2

1

You're seeing this error because the closure captures an immutable self.

Just like primitive types (e.g. Int), structs are value-types, and Swift is built with the notion of immutability of value-types.

In other words, if you had let questionManager = QuestionManager(), you'd expect questionManager not to change. Even if it was a var, it can only mutate via direct action by the caller, e.g. questionManager.doMutatingFunc().

But, if a closure was allowed to capture self, it could modify itself at some later point. This is not allowed.

This simplest (only?) way to fix this is to turn QuestionManager into a class:

class QuestionManager {
   // ... 
}
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Thanks - It actually worked and I should have thought about it! The resulting "downloadedData" shows up as nil though (while I'm sure the parsing works well) - Do you think it depends from the networking time? Should I solve the problem with a completion handler? – MRoot Nov 14 '20 at 18:51
0

struct is a value type. For value types, only methods explicitly marked as mutating can modify the properties of self, so this is not possible within a computed property.

If you change struct to be a class then your code compiles without problems.

Structs are value types which means they are copied when they are passed around.So if you change a copy you are changing only that copy, not the original and not any other copies which might be around.If your struct is immutable then all automatic copies resulting from being passed by value will be the same.If you want to change it you have to consciously do it by creating a new instance of the struct with the modified data.

From https://stackoverflow.com/a/49253452/11734662

swifthing
  • 440
  • 4
  • 11