简体   繁体   English

SwiftUI 故障,索引超出范围

[英]SwiftUI trouble with index out of range

I know its a really simple question but I'm just stuck on it atm so any advice would be greatly appreciated as I am new to SwiftUI.我知道这是一个非常简单的问题,但我只是坚持使用 atm,所以任何建议都将不胜感激,因为我是 SwiftUI 的新手。

I am trying to download text from firebase and render it to the view but I keep getting an out of range error:我正在尝试从 firebase 下载文本并将其渲染到视图中,但我不断收到超出范围的错误:

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444致命错误:索引超出范围:文件 Swift/ContiguousArrayBuffer.swift,第 444 行

The code is as follows:代码如下:

var body: some View{
    
    ZStack {
        
        if fetch.loading == false {
            LoadingView()
        }
        
        else{
            Text(names[0])
                .bold()
        }
    }
    .onAppear {
        self.fetch.longTask()
    }
    
}

Here is the Fetch Content Page:这是获取内容页面:

@Published var loading = false

func longTask() {

    DispatchQueue.main.asyncAfter(deadline: .now()) {
        
        let db = Firestore.firestore()
        db.collection("Flipside").getDocuments { (snapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
                
                return
            } else {
                for document in snapshot!.documents {
                    let name = document.get("Name") as! String
                    let description = document.get("Description") as! String
                    //name = items[doc]
                    print("Names: ", name)
                    print("Descriptions: ", description)
                    names.append(name)
                    descriptions.append(description)
                }
            }
        }
        self.loading = true
    }
}

So basically when the view appears, get the data from Firebase when the data has downloaded display the menuPage() until then show the Loading Data text.所以基本上当视图出现时,在数据下载后从 Firebase 获取数据,然后显示menuPage()直到显示Loading Data文本。

Any help is welcome!欢迎任何帮助!

As Rob Napier mentioned, the issue is that you're accessing the array index before the array is populated.正如 Rob Napier 提到的,问题是您在填充数组之前访问数组索引。

I'd suggest a couple of improvements to your code.我建议对您的代码进行一些改进。 Also, instead of maintaining separate arrays ( names , descriptions , ...) you can create a struct to hold all the properties in one place.此外,您可以创建一个结构来将所有属性保存在一个位置,而不是维护单独的 arrays ( namesdescriptions ,...)。 This will allow you to use just one array for your items.这将允许您只为您的项目使用一个数组。

struct Item {
    let name: String
    let description: String
}
class Fetch: ObservableObject {
    @Published var items: [Item] = [] // a single array to hold your items, empty at the beginning
    @Published var loading = false // indicates whether loading is in progress

    func longTask() {
        loading = true // start fetching, set to true
        let db = Firestore.firestore()
        db.collection("Flipside").getDocuments { snapshot, err in
            if let err = err {
                print("Error getting documents: \(err)")
                DispatchQueue.main.async {
                    self.loading = false // loading finished
                }
            } else {
                let items = snapshot!.documents.map { document in // use `map` to replace `snapshot!.documents` with an array of `Item` objects
                    let name = document.get("Name") as! String
                    let description = document.get("Description") as! String
                    print("Names: ", name)
                    print("Descriptions: ", description)
                    return Item(name: name, description: description)
                }
                DispatchQueue.main.async { // perform assignments on the main thread
                    self.items = items
                    self.loading = false // loading finished
                }
            }
        }
    }
}
struct ContentView: View {
    @StateObject private var fetch = Fetch() // use `@StateObject` in iOS 14+

    var body: some View {
        ZStack {
            if fetch.loading { // when items are being loaded, display `LoadingView`
                LoadingView()
            } else if fetch.items.isEmpty { // if items are loaded empty or there was an error
                Text("No items")
            } else { // items are loaded and there's at least one item
                Text(fetch.items[0].name)
                    .bold()
            }
        }
        .onAppear {
            self.fetch.longTask()
        }
    }
}

Note that accessing arrays by subscript may not be needed.请注意,可能不需要通过下标访问 arrays。 Your code can still fail if there's only one item and you try to access items[1] .如果只有一个项目并且您尝试访问items[1] ,您的代码仍然会失败。

Instead you can probably use first to access the first element:相反,您可能可以使用first来访问第一个元素:

ZStack {
    if fetch.loading {
        LoadingView()
    } else if let item = fetch.items.first {
        Text(item.name)
            .bold()
    } else {
        Text("Items are empty")
    }
}

or use a ForEach to display all the items:或使用ForEach显示所有项目:

ZStack {
    if fetch.loading {
        LoadingView()
    } else if fetch.items.isEmpty {
        Text("Items are empty")
    } else {
        VStack {
            ForEach(fetch.items, id: \.name) { item in
                Text(item.name)
                    .bold()
            }
        }
    }
}

Also, if possible, avoid force unwrapping optionals.此外,如果可能,请避免强制展开选项。 The code snapshot..documents will terminate your app if snapshot == nil .如果snapshot == nil ,代码snapshot..documents将终止您的应用程序。 Many useful solutions are presented in this answer:此答案中提供了许多有用的解决方案:

The basic issue is that you're evaluating names[0] before the names array has been filled in. If the Array is empty, then you would see this crash.基本问题是您在填充names数组之前评估names[0] 。如果数组为空,那么您会看到此崩溃。 What you likely want is something like:你可能想要的是这样的:

Item(title: names.first ?? "", ...)

The reason you're evaluating names[0] too soon is that you call completed before the fetch actually completes.您过早评估names[0]的原因是您在获取实际完成之前调用completed You're calling it synchronously with the initial method call.您正在与初始方法调用同步调用它。

That said, you always must consider the case where there are connection errors or or the data is empty or the data is corrupt.也就是说,您始终必须考虑存在连接错误或数据为空或数据已损坏的情况。 As a rule, you should avoid subscripting Arrays (preferring things like .first ), and when you do subscript Arrays, you must first make sure that you know how many elements there are.通常,您应该避免下标.first (更喜欢 .first 之类的东西),并且当您下标 Arrays 时,您必须首先确保您知道有多少元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM