1

I'm a newbie in swift and I'm trying to pass an instance of a class (CP) I made as a property of the ContentView struct for swift UI. This instance takes in parameter another property of the same ContentView struct. I want to be able to select an option from the alert menu, set the property to a variable that will determinate specific time sequences from a chronometer, and since the parameter of this instance is an inout string, the values will update in the instance. I found somebody in a similar situation on this website, and somebody suggested overriding the viewDidLoad function. However this does not apply to the SwiftUI framework.

Here is a sample of the code I believe is enough to understand the issue. débatCP, will be used in other elements so declaring it inside the first button action will not work.

struct ContentView: View {
@State var a = "Chronometre"
@State var partie = "Partie du débat"
@State var tempsString = "Temps ici"
@State var enCours = "CanadienParlementaire - Commencer"
@State var pausePlay = "pause"
@State var tempsMillieu = 420;
@State var tempsFermeture = 180
@State var round = 7;
@State var répartitionPM = "7/3"
var debatCP = CP(modePM: &répartitionPM)
@State private var showingAlert = false
//il va falloir un bouton pause
var body: some View {
    VStack {
        Text(String(self.round))
        Text(a)
            .font(.largeTitle)
            .fontWeight(.semibold)
            .lineLimit(nil)

        Text(partie)
        Button(action: {
            self.showingAlert = true
            if self.enCours != "Recommencer"{
            let chrono = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (chrono) in
                self.debatCP.verifierEtatDebut(round: &self.round, tempsActuel: &self.tempsMillieu, pause: &self.pausePlay, partie: &self.partie, tempsStr: &self.tempsString)
                if self.round <= 2{
                     self.debatCP.verifierEtatFin(round: &self.round, tempsActuel: &self.tempsFermeture, pause: &self.pausePlay, partie: &self.partie, tempsStr: &self.tempsString)

                }
                if self.round <= 0 {
                    chrono.invalidate()
                    self.enCours = "Canadien Parlementaire - Commencer"
                }
            }
            }
            else{
                self.tempsMillieu = 420
                self.tempsFermeture = 180
                self.round = 7
            }
            self.enCours = "Recommencer"

            self.a = "CP"
        }, label: {
            Text(enCours)
                .alert(isPresented: $showingAlert) {
                    Alert(title: Text("6/4 ou 7/3"), message: Text("6/4 pour avoir plus de temps à la fin et 7/3 pour en avoir plus au début"), primaryButton: .default(Text("6/4"), action: {
                        self.répartitionPM = "6/4";
                    }), secondaryButton: .default(Text("7/3"), action: {
                        self.répartitionPM = "7/3"
                    }))
            }
        })

I get this error message : Cannot use instance member 'répartitionPM' within property initializer; property initializers run before 'self' is available

Edit

Here's the CP class

class CP:Debat{
init(modePM: inout String){
}
  override  var rondeFermeture:Int{
        return 2;
    }
    override var tempsTotalMillieu : Int{
        return 420;
    };
    override var tempsProtege:Int{//60
        return 60;
    }

    override var tempsLibre:Int{//360
        return 360;
    }
    override var tempsFermeture: Int{
        return 180;
}

Along with a sample of the Defat Class

class Debat{
var rondeFermeture:Int{
    return 0;
}
var tempsTotalMillieu:Int{//420
    return 0;
};
var tempsProtege:Int{//60
    return 0;
}
var tempsLibre:Int{//360
    return 0;
}
var tempsFermeture:Int{
    return 0
}
func formatTime(time:Int) -> String {
    let minutes:Int = time/60
    let seconds:Int = time%60
    return String(minutes) + ":" + String(seconds)
}
func prochainTour(time: inout Int, round : inout Int)->Void{
    if round > rondeFermeture {
    let tempsAvantLaFin = time % self.tempsTotalMillieu
        time -= tempsAvantLaFin
    }
    if round <= rondeFermeture{
        let tempsAvantLaFin = time % self.tempsFermeture
        time -= tempsAvantLaFin
    }
}
Louis Couture
  • 72
  • 1
  • 9
  • It's an inout string `var debatCP = CP(modePM: &"7/3")`would returnCannot pass immutable value as inout argument: literals are not mutable – Louis Couture Feb 28 '20 at 22:16

1 Answers1

1
@State var répartitionPM = "7/3"
var debatCP = CP(modePM: &répartitionPM)

The SwiftUI has different pattern to work with @State variables. It is not clear what is CP but anyway the following is the way to go

struct AContentView: View {

    @State var repartitionPM = "7/3" // should be defined
    var debatCP:CP!=nil                  // declare-only

    init() {
        debatCP = CP(modePM: $repartitionPM) // pass as binding
    }
    ...

and CP should be something like

class CP:Debat{
@Binding var modePM:String

init(modePM:Binding <String>){
    self._modePM = modePM
    super.init()

}
Louis Couture
  • 72
  • 1
  • 9
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • CP is a class, which inherits from another one, so I don't think structs could be useful there. `init(){ debatCP = CP(modePM:&répartitionPM) }` makes this error 'self' used before all stored properties are initialized – Louis Couture Feb 29 '20 at 16:10
  • @LouisCouture, class can also contain `@Binding`, and pay attention, I put binding in `init`, not reference, and other comments are important. – Asperi Feb 29 '20 at 16:15
  • It works! However the current syntax needs that all properties are initialized before we use it (see https://stackoverflow.com/questions/34474545/self-used-before-all-stored-properties-are-initialized). This can be solved by setting debatCP to nil and making its declaration mandatory using ! Also the syntax to assign a binding var to a class is different. I will modify your answer to reflect the changes – Louis Couture Feb 29 '20 at 18:21