Runtime error: precondition failure: attribute failed to set an initial value
Asked Answered
P

9

14

I have a view BugSplitView which works fine alone but causes a

precondition failure: attribute failed to set an initial value

error when navigated to in either preview or the simulator.

The view has an upper part (Color) and a lower part (Color) separated by a horizontal button bar and laid out using the GeometeryReader and a split state. When it is the destination of NavigationButton it doesn't show properly in the Preview and reports the assertion above when run in the simulator. Remove the BugButtonBar and it works. Got me stumped! Help.

import SwiftUI

struct BugSplitView: View {
    @State var split : CGFloat = 0.75
    var buttons : [BugButtonBar.Info]{
        [BugButtonBar.Info(title: "title", imageName: "text.insert"){}]
    }
    var body: some View {
        GeometryReader{ g in
            VStack(spacing: 0){
                Color.gray
                    .frame(width: g.size.width, height: (g.size.height) * self.split)
                VStack{
                    BugButtonBar(infos: self.buttons)
                    Color(white: 0.3)
                }
                    .frame(height: (g.size.height) * (1 - self.split))
            }
        }.edgesIgnoringSafeArea(.all)
    }
}


struct BugButtonBar : View{

    struct Info : Identifiable {
        var id = UUID()
        var title : String
        var imageName : String
        var action: () -> Void
    }

    var infos : [Info]
    func color() -> Color{
        Color.black
    }
    var body: some View {
        HStack(){
            Spacer()
            ForEach(self.infos){ info in
                Button(action: info.action){
                    Text(info.title)
                }
                Spacer()
            }
        }
    }
}


struct ShowBugView : View{
    var body : some View{
        NavigationView {
            NavigationLink(destination: BugSplitView()){
                Text("Show Bug")
            }
        }
    }
}


struct BugSplitView_Previews: PreviewProvider {
    static var previews: some View {
        Group{
            BugSplitView()
            ShowBugView()
        }
    }
}
Puggree answered 26/10, 2019 at 18:39 Comment(0)
P
6

The problem is that your buttons are declared as computed property. To solve the crash declare them like this:

var buttons = [BugButtonBar.Info(title: "title", imageName: "text.insert"){}]
Phillisphilly answered 26/10, 2019 at 20:58 Comment(6)
Thank you. I think you answer complements mine below. Since it was computed, the new id each time confused swift.Puggree
The disadvantage of this approach is that the action code can't refer to the enclosing struct's vars since self is not yet available.Puggree
@Phillisphilly Can you explain why this works as a solution?Ashla
I experienced the same issue and requested TSI in order the get the explanation why is that a solution. Apple response below: The SwiftUI framework has two guiding principles— Every time a piece of data is read within a view, a dependency is created between the view and the accessed data. Every piece of data that is read in the view hierarchy should have a single source of truth.Manxman
In pure Swift, this change should have no affect on the structure produced, however, there may be a bug within how these mutations are handled in SwiftUI. I would suggest submitting a bug report via Feedback Assistant (feedbackassistant.apple.com) under SwiftUI, and using the workaround until we can take a further look.Manxman
Bottom line: the answer is a legit workaround for the issue, but submit a bug report every time you experience something simmilar.Manxman
P
4

Turns out the id property of struct Info was the problem. Changed it to a computed property as follows:

var id : String {
   title + imageName
}

Great example of why I love/hate SwiftUI.

Puggree answered 26/10, 2019 at 21:39 Comment(1)
Why is this changing anything... This made it work for me as wellKylakylah
B
4

For me it was displayMode inline in navigation bar title. Removing it fixes this problem.

Crash

.navigationBarTitle("Title", displayMode: .inline)

No crash

.navigationBarTitle("Title")
Belfort answered 21/2, 2020 at 20:4 Comment(1)
Thank you! For some reason, I only had the crash when VoiceOver was enabled, it worked normally. Removing displayMode fixed the crash when VoiceOver was enabled. I have no idea how you figured that out.Practical
L
2

Since it seems that this error - which can't be directly debugged - can be caused by so many different issues, I figured I'd throw my case up here too.

In my case, the error I was getting was:

precondition failure: attribute failed to set an initial value - 128

The issue was that I was attempting to present a sheet on a VStack that contained a NavigationView inside of it, like the below:

var body: some View {
    VStack(alignment: .center) {
        if /* some condition */ {
            /* some other content */
        } else {
            NavigationView {
                /* view content */
            }
        }
    }.sheet(isPresented: /* Binding value */) { 
        /* sheet content */
    }
}

The fix was to make sure that the sheet was being presented on the NavigationView instead:

var body: some View {
    NavigationView {
        VStack(alignment: .center) {
            if /* some condition */ {
                /* some other content */
            } else {
                /* view content */
            }
        }
    }.sheet(isPresented: /* Binding value */) { 
        /* sheet content */
    }
}

Seems obvious in hindsight, but it would have been nice to get a bit more information when the crash occurred in the first place.

Lonee answered 26/2, 2020 at 18:20 Comment(0)
E
2

I had this error. In my case, it was caused by having a NavigationView inside both blocks of an if-else statement.

// bad
if someBool {
  NavigationView {
    Text("some content")
  }
} else {
  NavigationView {
    Text("different content")
  }
}
// good
NavigationView {
  if someBool {
    Text("some content")
  } else {
    Text("different content")
  }
}
Exclaim answered 14/3, 2020 at 13:58 Comment(0)
I
2

In my case it was setting a value to a binding property when view disappears, a property that changes a view like this:

  .onDisappear(perform: {
    withAnimation(.easeInOut) {
        self.action.collageWidthSize = 2.0 /* modifies next view border */
      }
   })

Setting this in the next view's onAppear fixed it:

   .onAppear {
        withAnimation(.easeInOut) {
            self.action.collageWidthSize = 2.0
        }
    }
Inconsiderate answered 28/6, 2020 at 8:56 Comment(0)
F
0

Ok, I was bit by this. Xcode 11.6.

My views are probably a bit convoluted, but what i'm doing is if a user puts the view into an 'edit' mode all of the cells change their presentation. I was getting this error seemingly at random when i switched back. I fixed it (fingers still crossed) by removing an unnecessary binding. I was passing a boolean binding into some of the subviews so that they know what state things are in, and how to be presented. Thing is, they don't need to respond to the boolean change, because the parent is being redrawn anyway, and it can just recreate the children. They don't need to be notified that they should change state, they are simply destroyed and recreated.

Felipe answered 3/9, 2020 at 23:24 Comment(0)
S
0

I used NavigationView as the root of TabView:

NavigationView {
   TabView {
   }
}

Then in the TabView I used NavigationView too, so due to this error. The solution is only use one NavigationView.

Singlestick answered 29/1, 2021 at 2:44 Comment(0)
S
0

I had the same problem. I just removed the random id (UUID) from the Identifiable and I fixed my crash. Maybe it could help others

Scintillation answered 8/3, 2023 at 18:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.