Property Initializers Run Before 'Self' Is Available

Property initializers run before 'self' is available

As correctly pointed out by vadian you should create an init in such scenarios:

class MyOwn {
let myUser: User
var life: Int

init() {
self.myUser = User(name: "John", age: 100)
self.life = myUser.age
}
}

You can't provide a default value for a stored property that depends on another instance property.

Cannot use instance member 'service' within property initializer; property initializers run before 'self' is available

Basically, what the compiler is saying is that "I'm still creating the instance of your DetailCardView, I don't know yet what is the value of service so I can't use it in the region".

The solution is to pass the service to a constant that will be used to initialise both properties. You need to create an initializer for your View where you pass this constant.

Here's how it looks:

let service: gService


// Rather than service.longitude and service.latitude, use a dummy value, like 0.0
// Recommended this var to be private
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0), span: MKCoordinateSpan(latitudeDelta: 0.0033, longitudeDelta: 0.0033))

// Here's your initializer
init(service: gService) {
self.service = service // Use service to initialise self.service

// Use service - and NOT self.service - to initialise region
region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: service.latitude, longitude: service.longitude), span: MKCoordinateSpan(latitudeDelta: 0.0033, longitudeDelta: 0.0033))
}

var body: some View {
... // The rest of your code

Another method you can try is to drop the init() and set the region when the view appears, like this:

let service: gService


// Rather than service.longitude and service.latitude, use a dummy value, like 0.0
// Recommended this var to be private
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0), span: MKCoordinateSpan(latitudeDelta: 0.0033, longitudeDelta: 0.0033))

var body: some View {
Map(coordinateRegion: $region, annotationItems: [MapPoint(coordinates: CLLocationCoordinate2D(latitude: service.latitude, longitude: service.longitude))]) { location in
MapAnnotation(coordinate: location.coordinates) {
Circle()
.stroke(.red, lineWidth: 3)
.frame(width: 50, height: 50, alignment: .center)
}
.onAppear {
region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: service.latitude, longitude: service.longitude), span: MKCoordinateSpan(latitudeDelta: 0.0033, longitudeDelta: 0.0033))
}
}

SwiftUI Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available error

You can use read-only computed property with short-hand.

var text: String { 
return (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}

Cannot use instance member 'getA' within property initializer; property initializers run before 'self' is available

Property initializer run before self is available.

The solution is to lazy initialize the property:

class A {
lazy var asd: String = getA()

func getA() -> String {
return "A"
}
}

That will initialize the property first time you are trying to use it.

Cannot use instance member 'videoName' within property initializer; property initializers run before 'self' is available

Option 1:

Create an initializer for your View that creates your @State initial value:

struct ExercisingSessionView: View {

let exerciseName: String
let videoName: String

@State var player : AVPlayer
@State var isplaying = false
@State var showcontrols = false

init(exerciseName: String, videoName: String) {
self.exerciseName = exerciseName
self.videoName = videoName
self._player = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!)))
}

var body: some View {
CustomVideoPlayer(player: $player)
.frame(width: 390, height: 219)
.onTapGesture {
self.showcontrols = true
}
}
}

The downside to this is if ExercisingSessionView gets initialized often (even if it doesn't get actually re-rendered to the view hierarchy), you're doing heavy lifting inside init, which is generally a pretty bad idea for performance.

Option 2:

Declare player as optional and load the initial value in onAppear:



struct ExercisingSessionView: View {

let exerciseName: String
let videoName: String

@State var player : AVPlayer?
@State var isplaying = false
@State var showcontrols = false

var body: some View {
Group {
if let player = player {
CustomVideoPlayer(player: player)
.frame(width: 390, height: 219)
.onTapGesture {
self.showcontrols = true
}
}
}.onAppear {
player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoName, ofType: "mov")!))
}
}
}

struct CustomVideoPlayer : UIViewControllerRepresentable {
var player: AVPlayer

func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {

let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
return controller
}

func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {

}
}

This avoids the issue in option 1 because onAppear will only be called once.

Note that here, I've made player inside CustomVideoPlayer a regular, non-binding property -- because AVPlayer is an class, passed by reference, there's no reason to have a @Binding with it.

SwiftUI: Property initializers run before 'self' is available in @AppStorage use

You can't refer to $currency when you define cur. This is a limitation of Swift. It obvious to you that currency has a value already, but Swift won't let you use it until after the initializer is done setting up self. This has nothing to do with AppStorage -- it's a rule that property initializers cannot refer to other (non-static) properties.

So, this

@State private var currency = "$"
@AppStorage("cur") var cur = "\($currency)"

Could be

@State private var currency = "$"
@AppStorage("cur") var cur = "$" // set it to the same value

or

private static let dollar = "$"

@State private var currency = SettingsView.dollar
@AppStorage("cur") var cur = SettingsView.dollar


Related Topics



Leave a reply



Submit