Home > Net >  SwiftUI class init mistake
SwiftUI class init mistake

Time:11-11

I'm a new beginner in swiftUI and I'm trying to deal with a Class that use CoreLocation to do some places locations comparison. But I've add my structured array of place in my Class and I've got an error with the override init().

My class :

import Foundation
import CoreLocation
import Combine
import SwiftUI

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {

    private let locationManager = CLLocationManager()
    
    @ObservedObject var placeLibrary: PlaceLibrary
    
    @Published var locationStatus: CLAuthorizationStatus?
    @Published var lastLocation: CLLocation?
    @Published var distanceFromNearest: Double = 0.0
    @Published var nearestObject:String = ""
    
    override init() {
        placeLibrary.testPlace = placeLibrary.testPlace
        super.init() // HERE I GET MY ERROR 
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
        self.placeLibrary = placeLibrary
    }
    
    var statusString: String {
        guard let status = locationStatus else {
            return "unknown"
        }
        
        switch status {
        case .notDetermined: return "notDetermined"
        case .authorizedWhenInUse: return "authorizedWhenInUse"
        case .authorizedAlways: return "authorizedAlways"
        case .restricted: return "restricted"
        case .denied: return "denied"
        default: return "unknown"
        }
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        locationStatus = status
        print(#function, statusString)
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        lastLocation = location
        
        for (idx, readOnlyPlace) in placeLibrary.testPlace.enumerated() {
            // Calculate stuff
            let currentLocation = CLLocation(latitude: (self.lastLocation?.coordinate.latitude) ?? 0.0, longitude: (self.lastLocation?.coordinate.longitude) ?? 0.0)
            let comparedLocation = CLLocation(latitude: readOnlyPlace.lat, longitude: readOnlyPlace.long)

            // Update struct
            placeLibrary.testPlace[idx].proximity = currentLocation.distance(from: comparedLocation)
        }
        
        placeLibrary.testPlace = placeLibrary.testPlace.sorted(by: { $0.proximity < $1.proximity })
        
        print(placeLibrary.testPlace)
    }
}

The error result here is : Property 'self.placeLibrary' not initialized at super.init call

After looking on internet I understand that I need to define all my variable used by my Class into the Init. That's why I add this line without any success : self.placeLibrary = placeLibrary even if there is before or after the super.init() line...

So I think there something I don't understand ...

My Place library :

class PlaceLibrary: ObservableObject{
    
    @Published var testPlace = [
        Place(lat: 46.1810, long: 6.2304, Name: "Place 1", proximity: 0.0),
        Place(lat: 46.1531, long: 6.2951, Name: "Place 2", proximity: 0.0),
        Place(lat: 46.1207, long: 6.3302, Name: "Place 3", proximity: 0.0)
    ]
}

My Place structure :

struct Place: Identifiable{
    let id = UUID().uuidString
    var lat: Double
    var long: Double
    var Name: String
    var proximity: Double
    
    init (lat: Double, long: Double, Name: String, proximity: Double){
        self.lat = lat
        self.long = long
        self.Name = Name
        self.proximity = proximity
    }
    
    init(config: NewPlaceConfig){
        self.lat = config.lat
        self.long = config.long
        self.Name = config.Name
        self.proximity = config.proximity
    }
}

And finally my NewPlaceConfig

struct NewPlaceConfig{
    var lat: Double
    var long: Double
    var Name: String
    var proximity: Double
}

CodePudding user response:

The placeLibrary property is, like the error messages says, not initialized.

ObservedObject, in this instance, does nothing. The @ObservedObject property wrapper should only be used in View structs. It tells the View to observe the object for any changes, so that Swift knows when to redraw or recalculate the view. Other than that, it does nothing in regards to initialization.

So, really, the property is declared as var placeLibrary: PlaceLibrary. This declaration does not initialize the property, it only describes the type that it will hold.

You can either initialize it yourself before the super.init call using self.placeLibrary = PlaceLibrary(), or, more concisely, initialize it at the property declaration site itself:

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()
    
    var placeLibrary = PlaceLibrary()
    
    // ...

    // Alternatively, initialize the property in the init()
    override init() {
        self.placeLibrary = PlaceLibrary()
        super.init()
        // ...
        // Remaining initialization code
    }
}

I would also replace var with let, but that is your choice.

CodePudding user response:

Property 'self.placeLibrary' not initialized at super.init call

The problem is not with your super.init() call, but because you are trying to access a property of a class before this class is fully initialized (like the message says).

Edit: This seems odd at first. But you cannot guarantee the order that the compiler will call and initialize your class. You can only have certain that a class is fully initialized after init() finishes. That's why you cannot use properties inside the init, by definition, only initialize.

Edit2: Easiest way to fix is make optional. If you have certain that this property will never go back to nil again you can make it automatically upwrapped with !.

Edit3: Something like this:

import Foundation
import CoreLocation

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    
    private var number: Int!
    
    override init() {
        super.init()
    }
    
    convenience init(test: Int) {
        self.init()
        self.number = test
    }
}

class MyClass {
    var location: LocationManager
    
    init(location: LocationManager) {
        self.location = location
    }
}

Note the optional property here Int!

  • Related