Search code examples
swiftuisigabrtgeometryreader

SwiftUI - GeometryReader crashes SIGABRT


I'm trying to implement two different views depending on the device width. So the iPad Version of this view should be different to the iPhone version. To do this, I use the GeometryReader to check for the width. However, the app always crashes with "Thread 1: signal SIGABRT". Each of the views on their own work perfectly fine.

If I start it in Splitscreen for iPad with a width less than 592, it works fine. I can change it to the big size afterwards without a crash. If start with a width greater than 592, it crashes.

Also if I only use the if statement without the else, it works.

Even the test on top crashes.

Here my code:

import SwiftUI

struct DetailView: View {
    
    let food: FoodList
    
    @State var showRightMenu = false
    
    var body: some View {
        GeometryReader { bounds in
            ZStack (alignment: .topLeading) {

                // Test
                if bounds.size.width > 592 {
                    Text("Test")
                } else {
                    Text("Test1")
                    Text("Test2")
                }

                // Actual code
//                if bounds.size.width > 592 {
//                    HStack {
//                        FoodDetailPadViewLeft(food: self.food)
//                            .frame(width: bounds.size.width / 2)
//
//                        FoodDetailPadViewRight(food: self.food)
//                    }
//                } else {
//                    ScrollView {
//                        FoodDetailViewImage(food: self.food)
//                            .animation(.none)
//
//                        FoodDetailViewNutris(food: self.food)
//
//                        Spacer()
//                    }
//                }
                
                HStack {
                    BackButton()
                    Spacer()
                    InfoButton(showRightMenu: self.$showRightMenu)
                }
            }
            .background(Color("background"))
            .edgesIgnoringSafeArea(.all)
            .navigationBarTitle("")
            .navigationBarHidden(true)
            .navigationBarBackButtonHidden(true)
        }
    }
}

Here is some reproducible code:

import SwiftUI

struct ContentView: View {
    @State var foodlist: [FoodList] = Bundle.main.decode("ingredientsList.json")
    
    var body: some View {
        
        NavigationView {
            List {
                ForEach(foodlist) { food in
                    NavigationLink (destination: TestView(food: food)) {
                        Text(food.name)
                    }
                }
            }
        }
    }
}


struct TestView: View {
    let food: FoodList
    
    var body: some View {
        GeometryReader { bounds in
            ZStack (alignment: .topLeading) {
                
                if bounds.size.width > 592 {
                    Text(self.food.name)
                } else {
                    Text(self.food.name)
                    Text(self.food.category)
                }
            }
        }
    }
}



struct FoodList: Codable, Identifiable, Hashable {
    let id: Int
    let category: String
    let name: String
}


extension Bundle {
    func decode<T: Codable>(_ file: String) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }
        
        guard let data = try? Data (contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }
        
        let decoder = JSONDecoder()
        
        guard let loaded = try? decoder.decode(T.self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }
        
        return loaded
    }
}

and the Json-File:

[
    {
      "id": 1,
      "category": "vegetables",
      "name": "Tomato",
    },
    {
      "id": 2,
      "category": "vegetables",
      "name": "Potato",
    }
]

Any ideas?


Solution

  • This is that case when it is better to check explicitly for what you need:

    var body: some View {
        ZStack (alignment: .topLeading) {
            if UIDevice.current.userInterfaceIdiom == .pad {
                Text(self.food.name)
            } else {
                Text(self.food.name)
                Text(self.food.category)
            }
        }
    }
    

    Note: the crash happens due to changed layout on the stack, and that happens because GeometryReader on same layout stack got different values (in first turn it is .zero, and second it is real), so different branches of your condition are activated on same layout stack and this makes SwiftUI rendering engine crazy. (You can submit feedback for this to Apple, but it is hardly to be resolved, because it is chicken-egg problem - Geometry reader always go two times).