Charles Truluck
Charles Truluck

Reputation: 981

'self' captured by a closure before all members were initialized

Alright, so just to start off, heres my code:

import UIKit
import ForecastIO

class Weather {
    var temp: Float
    var condition: String
    var wind: Float
    var precip: Float

    init() {
        DarkSkyClient(apiKey: "<api key>").getForecast(latitude: Utils().getLat(), longitude: Utils().getLong()) { result in

            switch result {
            case .success(let currentForecast, _):

                self.temp = (currentForecast.currently?.temperature)!
                self.condition = (currentForecast.currently?.summary)!
                self.wind = (currentForecast.currently?.windSpeed)!
                self.precip = (currentForecast.currently?.precipitationProbability)!

            case .failure(let error):
                print(error)

            }

        }

    }

}

So my error comes up because I'm trying to initialize temp inside of the API call. I know this isn't the most reliable way of doing it but I'm trying to first get it to work.

The first error is:

'self' captured by a closure before all members were initialized

on the line DarkSkyClient(apiKey: "").getForecast(latitude: Utils().getLat(), longitude: Utils().getLong()) { result in

My second error:

Return from initializer without initializing all stored properties

on the second to last }

Now, obviously I'm not initializing right. I can't find the proper way to do what my end goal is though. Maybe I'm doing this entirely wrong?

Upvotes: 26

Views: 35178

Answers (3)

Winter
Winter

Reputation: 1114

For me, it's because I didn't call super.init() in the initializer.

class AnObject: NSObject {
    override init() {
//        super.init()

        let _: ()-> (Void) = {
            print(String(describing: self))
        }
    }
}

Upvotes: 43

omarzl
omarzl

Reputation: 959

You have two options, declare the properties as optionals, or initialize them with a default value (this means they will be non-optionals)

var temp: Float?
var condition: String?
var wind: Float?
var precip: Float?

or

var temp: Float=0
var condition: String=""
var wind: Float=0
var precip: Float=0

Upvotes: 15

Ras Thavas
Ras Thavas

Reputation: 120

I'd hazard to guess that your are running into a concurrency problem. You are probably trying to access your object's properties before the asynchronous call to the DarkSkyClient returns (my apologies in advance if I got this wrong). i.e., the order of events is...

  1. Weather object is initialized, setting temp to 0
  2. Call to DarkSkyClient begins, runs in the background
  3. Read temp variable - hey, it's 0!
  4. Call to DarkSkyClient completes, sets the temp to the value you really wanted. Bad

So what you really need to do is switch to an inversion of control pattern:

class Weather {
    var temp: Float
    var condition: String
    var wind: Float
    var precip: Float

    init(forecast: Forecast) {
        temp = (forecast.currently?.temperature)!
        condition = (forecast.currently?.summary)!
        wind = (forecast.currently?.windSpeed)!
        precip = (forecast.currently?.precipitationProbability)!
    }

    static func getWeather() {
        DarkSkyClient(apiKey: "<api key>").getForecast(latitude: Utils().getLat(), longitude: Utils().getLong()) { result in

            switch result {
            case .success(let currentForecast, _):
                let weather = Weather(forecast: currentForecast)
                // Display the weather somewhere
                doSomethingWith(weather: weather)
            case .failure(let error):
                print(error)
            }
        }
    }    
}

If you're not familiar with developing with asynchronous APIs it's worth your while to read up on the subject; it can be very tricky (sadly, I don't have any recommendations for a good primer). Hope this helps!

Upvotes: 7

Related Questions