Diesel
Diesel

Reputation: 5353

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

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.

Upvotes: 6

Views: 14520

Answers (4)

Jeffrey Fulton
Jeffrey Fulton

Reputation: 4146

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.

Upvotes: 14

Johnny
Johnny

Reputation: 1964

I have the same questions as you and I solve it using lazy var
Here is a quick example

My originally code is:

class MyClass {
    let callback:()->() = {
        self.foo()   // Compile error: Use of unresolved identifier "self"
    }

    func foo() {
        print("bar")
    }
}

It compile error at that line I use self
But I change it to

class MyClass {
    lazy var callback:()->() = {
        [unowned self] in
        self.foo()
    }

    func foo() {
        print("bar")
    }
}

let c = MyClass()
c.callback()

that solved the problem

References: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
http://mikebuss.com/2014/06/22/lazy-initialization-swift/
Shall we always use [unowned self] inside closure in Swift

Upvotes: 2

Vamshi Krishna
Vamshi Krishna

Reputation: 989

Replace var locations = [Location]() with this var locations : [Location]?

and var location : Location! = Location() with self.locations = [Location]() and self.locations.append(location) with self.locations?.append(location)

You will be good to go!

lazy var is too complex a concept to grasp I guess but you can use it this way:

lazy var locations:[Location] = {
        let locations = Location()
        return locations
    }()

Upvotes: 0

Rahul
Rahul

Reputation: 2100

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 }

Upvotes: 6

Related Questions