Variable Used Before Being Initialized in Function

variable used before being initialized

This is because of the way of initialising variables in Swift, In Swift Class each property need some default value before being used.

Class initialisation in Swift is a two-phase process. In the first
phase, each stored property is assigned an initial value by the class
that introduced it. Once the initial state for every stored property
has been determined, the second phase begins, and each class is given
the opportunity to customize its stored properties further before the
new instance is considered ready for use.

Try changing your code as below:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destViewController = segue.destinationViewController as! SecondTableViewController
var secondArrays : SecondTableData?

if let indexPath = self.tableView.indexPathForSelectedRow {
secondArrays = secondArrayData[indexPath.row]

//Here you're making sure that the secondArrays would must have a value
destViewController.secondArray = secondArrays.secondTitle
}

}

False error? `Variable used before being initialized` DURING initialization in init()

This code:

@State var greeting: String

Creates a backing variable behind the scenes:

let _greeting: State<String>

If you want to initialize it yourself in init(), you can do:

init() {
_greeting = new State<String>(initialValue: "Hello, World!")
}

but since you have a constant, it is simpler to set it like this:

@State var greeting: String = "Hello, World!"

The 3rd variant works, because without @State there's no magic.

The 1st option doesn't work, because the code gets translated to:

init() {
self._greeting.wrappedValue = "Hello, World!"
}

and _greeting is not initialized at this point (no initialValue is specified). Yes, the error is misleading.

I don't know why the 2nd version works. No initialValue is specified, and the String has no implicit default value. Maybe this is a new feature of iOS 15 SwiftUI that should work, but doesn't work in all cases due to some limitations?

P.S. There is something like "preinit": all properties that are assigned with some expressions like length = Measurement<UnitLength>(...) are run there before init, but there's no access to it, it is internal.

Swift: Variable used before being initialized]

You need to initialise state itself, like

    _entryData = State(initialValue: 
EditEntryData(info: entry.infos,
value: String(value), date: entry.date, iOwe: iOwe))

Another Variable '__' used before being initialized question

That variable is a little different than normal because of its @State property wrapper. To initialize it here without that error, you need to do this:

self._string = State(initialValue: "Why doesn't this work????")

The _ lets you access string without the property wrapper. In fact, you're already doing this on the line above with your Binding with a similar strategy.

On your second example, string already has a default value of "", so the compiler isn't worried about it not being set yet.

Variable used before being initialized error (Swift)

Just do the following:

@State var item: Item

init(item: Item) {
_item = State(initialValue: item)
}

You can access all of the things you may need:

  • item
  • $item.name (equivalent to when you had $name, same for other below)
  • $item.imageData
  • $item.timestamp

Also, if you don't need to pass anything else in, you can also get rid of the initializer because it is an inferred memberwise intializer.

Reason for the error you were having:

self.item doesn't exist yet. You set the @State with _item, and that will make the item and $item variables.

We can see this because seeing the definition of State shows the following:

public var wrappedValue: Value { get nonmutating set }  // Normal value

/* ... */

public var projectedValue: Binding<Value> { get } // Binding value

So since you can't access self.item because _item hasn't been initialized yet, you get the error. That's why we set _item using State(initialValue:).

Variable 'varname' used before being initialized in SwiftUI]

This should work:

init(_ test: Test2) {
_t = State(initialValue: test) // Variable 'self.t' used before being initialized
}

@State is a property wrapper, so you need to assign value to the underlying property, thus _ .

Variable used by function before being initialized

Without a big refactor, the quickest changes may be explicitly passing the Binding to cityName through the views and to the UserLocationManager. That might look like this:

struct ContentView: View {
@State var cityName:String = ""
@State var temp:Double = 0
@State var condition:String = ""

var body: some View {
VStack{
UserLocatonButtonView(cityName: $cityName)
WeatherInfo(cityName: $cityName, temp: $temp, condition: $condition)
}
}
}

struct WeatherInfo: View {
@Binding var cityName: String
@Binding var temp: Double
@Binding var condition: String

var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}

struct UserLocatonButtonView: View {
@StateObject private var locationManager : UserLocationManager

init(cityName: Binding<String>) {
_locationManager = StateObject(wrappedValue: UserLocationManager(cityName: cityName))
}

var body: some View {
VStack {
if let location = locationManager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}

LocationButton(.currentLocation) {
locationManager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}

final class UserLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
var cityName : Binding<String>
@Published var location = CLLocation()

let locationManager = CLLocationManager()

init(cityName: Binding<String>) {
self.cityName = cityName
super.init()
locationManager.delegate = self
}

// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()

self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")

geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}

func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"

} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
cityName.wrappedValue = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}

That being said, I think a much cleaner solution would be moving the shared state to be owned by the parent view a model and @Published property. Also, perhaps it's just a result of your minimal example, but you only need to use Bindings when you need two-way communication. So, as it stands, your WeatherInfo doesn't need Bindings at all. Once such refactor might look like this:

struct Model {
var cityName : String
var temp: Double
var condition: String
}

struct ContentView: View {
@StateObject private var manager = UserLocationWeatherManager()

var body: some View {
VStack{
UserLocatonButtonView(manager: manager)
WeatherInfo(cityName: manager.model.cityName,
temp: manager.model.temp,
condition: manager.model.condition)
}
}
}

struct WeatherInfo: View {
var cityName: String
var temp: Double
var condition: String

var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}

struct UserLocatonButtonView: View {
@ObservedObject var manager : UserLocationWeatherManager

var body: some View {
VStack {
if let location = manager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}

LocationButton(.currentLocation) {
manager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}

final class UserLocationWeatherManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var model = Model(cityName: "", temp: 0, condition: "")
@Published var location = CLLocation()

let locationManager = CLLocationManager()

override init() {
super.init()
locationManager.delegate = self
}

// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()

self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")

geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}

func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"

} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
self.model.cityName = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}

Why do I get a Variable used before being initialized error on the line that I initialise the variable in Swift?

Swift has this behaviour because of two phase initialisation. From Apple's Swift book:

Class initialization in Swift is a two-phase process. In the first
phase, each stored property is assigned an initial value by the class
that introduced it. Once the initial state for every stored property
has been determined, the second phase begins, and each class is given
the opportunity to customize its stored properties further before the
new instance is considered ready for use.

Classes need some kind of default value before the first phase ends. Customising values is part of the second phase.

Objective-C didn't have this behaviour because it could always give 0 as default for primitives and nil for objects, but in Swift there is no mechanism to give such a default value.



Related Topics



Leave a reply



Submit