Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Use of unresolved identifier 'self' - from Closure in a Class

Tags:

swift

I am trying to reference a property in my class from a closure declared in my class. I cannot access self from inside my closure, and I'm assuming self would refer to the Class API from within my closure.

I want to declare a closure that I use later as a parameter to pass to a URLSession dataTask (It works without the one error line). I get the error listed in the title.

Use of unresolved identifier 'self'

I've been writing swift for an entire day now and am just trying things out as a sandbox, so I fully expect some criticism.

class Api {

    struct Location {
        var name = String()
        var author = String()
        var averageRating: String?
        var id = Int()
        var lat = Double()
        var lon = Double()
        var type = String()
    }

    var locations = [Location]()

    var doSomething = {(data: Data?, response: URLResponse?, error: Error?) -> Void in
        if error != nil {
            print(error!.localizedDescription)
        } else {
            do {
                if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
                    let myResult = json["results"] as! [[String: Any]]
                    var location : Location! = Location()
                    for jsonLocation in myResult {
                        if let name = jsonLocation["name"]{location.name = name as! String}
                        if let author = jsonLocation["author"]{location.author = author as! String}
                        if let id = jsonLocation["id"]{location.id = id as! Int}
                        if let lat = jsonLocation["lat"]{location.lat = lat as! Double}
                        if let lon = jsonLocation["lon"]{location.lon = lon as! Double}
                        if let type = jsonLocation["type"]{location.type = type as! String}

                        //ERROR IS HERE, Why does self not reference class API?
                        self.locations.append(location)
                    }
                }
            } catch {
                print("error in JSONSerialization")
            }
        }
    }
}

I have found this, but this example is different so I wasn't sure if it was the same bug or me not understanding swift.

like image 335
Diesel Avatar asked May 09 '17 13:05

Diesel


People also ask

What are closures in Swift?

Closures Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Closures can capture and store references to any constants and variables from the context in which they’re defined.

What is the argument to the sort method in Swift?

Because the sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns. The sorted (by:) method is being called on an array of strings, so its argument must be a function of type (String, String) -> Bool.

How do you refer to self in a closure?

Here’s a version of doSomething () that captures self by including it in the closure’s capture list, and then refers to self implicitly: If self is an instance of a structure or an enumeration, you can always refer to self implicitly.

What is the difference between nested functions and unnamed closures?

Nested functions are closures that have a name and can capture values from their enclosing function. Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.


2 Answers

Rahul's explanation is correct, but the suggested answer is ever so slightly incomplete.

Here is a complete solution:

  1. Declare the doSomething property as lazy as Rahul suggested. A lazy stored property is a property whose initial value is not calculated until the first time it is used. In other words this closure will not be evaluated until the doSomething property is called at run-time, at which point self is guaranteed to exist. See Lazy Stored Properties in the Swift Programming Language for more details.

  2. Add a type annotation to the doSomething property so the compiler doesn't have to infer the type at compile time, which apparently it can't do because the closure includes self. See Type Safety and Type Inference in the Swift Programming Language for more details.

So the complete declaration is:

...
lazy var doSomething: (Data?, URLResponse?, Error?) -> Void = { (data: Data?, response: URLResponse?, error: Error?) -> Void in
...

ps. Welcome to Swift programming! It's a fantastic language and really fun. I hope you enjoy it as much as I do.

like image 157
Jeffrey Fulton Avatar answered Sep 21 '22 09:09

Jeffrey Fulton


You are not able to access self because it is not available when you are calling inside the closure as initialization hasn't happened yet and so compiler gives you the error.

The fix would be to user lazy var as this will defer the self call because lazy var get called only after initialisation.

 lazy var doSomething = { your closure goes here }
like image 28
Rahul Avatar answered Sep 22 '22 09:09

Rahul