线程1:获取数据时EXC_BAD_INSTRUCTION

2024-01-07

我收到此错误 ->Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)随机的。我不太明白它到底是什么时候发生的。大多数时候是在视图刷新时。错误出现在以下行:group.leave()被处决。

我想做什么: 我想从我的 firebase 数据库中获取带有图像、名称和歌曲的专辑,这些专辑也有名称和图像。我检查了这些值,据我所知,它们都没有问题。但当试图向他们展示时,展示的内容是随机的。有时一切都是对的,有时一张专辑展示了两次,有时只展示了一张专辑,有时一张专辑包含另一张专辑的歌曲。

My firebase database has albums stored as documents, each document has albumimage/name and 2 subcollections of "unlocked" with documents(user uid) that store "locked":Bool and "songs" with a document for each song that stores image/name enter image description here

这是获取我的专辑及其歌曲的函数:

    let group = DispatchGroup()
    @State var albums: [Album] = []
    @State var albumSongs: [AlbumSong] = []

func fetchAlbums() {
FirebaseManager.shared.firestore.collection("albums").getDocuments { querySnapshot, error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            guard let documents = querySnapshot?.documents else {
                return
            }
            let uid = FirebaseManager.shared.auth.currentUser?.uid
            
            documents.forEach {  document in
                let data = document.data()
                let name = data["name"] as? String ?? ""
                let artist = data["artist"] as? String ?? ""
                let releaseDate = data["releaseDate"] as? Date ?? Date()
                let price = data["price"] as? Int ?? 0
                let albumImageUrl = data["albumImageUrl"] as? String ?? ""
                let docID = document.documentID
                FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("songs").getDocuments { querySnapshot, error in
                    if let error = error {
                        return
                    }
                    guard let documents = querySnapshot?.documents else {
                        return
                    }
                    self.albumSongs = documents.compactMap { document -> AlbumSong? in
                        do {
                            return try document.data(as: AlbumSong.self)
                        } catch {
                            return nil
                        }
                    }
                    group.leave()
                }
                FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("unlocked").document(uid ?? "").getDocument { docSnapshot, error in
                    if let error = error {
                        return
                    }
                    guard let document = docSnapshot?.data() else {
                        return
                    }
                    group.enter()
                        group.notify(queue: DispatchQueue.global()) {
                            if document["locked"] as! Bool == true {
                        self.albums.append(Album(name: name, artist: artist, 
songs: albumSongs, releaseDate: releaseDate, price: price, albumImageUrl: albumImageUrl))
                                print("albums: ",albums)
                            }
                        }
                }
            }
        }
}

我打电话给我的fetchAlbums()在我看来.onAppear()

我的专辑歌曲:

struct AlbumSong: Identifiable, Codable {
    @DocumentID var id: String? = UUID().uuidString
    let title: String
    let duration: TimeInterval
    var image: String
    let artist: String
    let track: String
    }

我的专辑:

struct Album: Identifiable, Codable {
    @DocumentID var id: String? = UUID().uuidString
    let name: String
    let artist: String
    let songs: [AlbumSong]
    let releaseDate: Date
    let price: Int
    let albumImageUrl: String
  }

我尝试研究如何使用异步函数从 firebase 获取数据,但我无法让我的代码工作,并且当我只有一张专辑时,使用dispatchGroup 工作得很好。我希望得到解释这段代码如何与异步一起工作的答案,我真的很长时间以来尽了最大努力自己弄清楚它。我也很想知道 DispatchGroup 究竟发生了什么,以及为什么它可以很好地处理一张专辑,但不能处理多张专辑。


我认为你把一些非常简单的事情变得过于复杂化了async await

首先,您的模型需要一些调整,这可能是您的一些问题的根源。

import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift

struct AlbumSong: Identifiable, Codable {
    //No need to set a UUID `@DocumentID` provides an ID
    @DocumentID var id: String?
    let title: String
    let duration: TimeInterval
    var image: String
    let artist: String
    let track: String
}
struct Album: Identifiable, Codable {
    //No need to set a UUID `@DocumentID` provides an ID
    @DocumentID var id: String?
    let name: String
    let artist: String
    //Change to var and make nil, the initial decoding will be blank
    //If any of the other variables might be optional add the question mark
    var songs: [AlbumSong]?
    let releaseDate: Date
    let price: Int
    let albumImageUrl: String
}

然后,您可以创建一项服务来使用 Firestore 完成繁重的工作。

struct NestedFirestoreService{
    private let store : Firestore = .firestore()
    
    let ALBUM_PATH = "albums"
    let SONG_PATH = "songs"
    ///Retrieves Albums and Songs
    func retrieveAlbums() async throws -> [Album] {
        //Get the albums
        var albums: [Album] = try await retrieve(path: ALBUM_PATH)
        //Get the songs, **NOTE: leaving the array of songs instead of making a separate collection might work best.
        for (idx, album) in albums.enumerated() {
            if let id = album.id{
                albums[idx].songs = try await retrieve(path: "\(ALBUM_PATH)/\(id)/\(SONG_PATH)")
            }else{
                print("\(album) :: has invalid id")
            }
        }
        //Add another loop for `unlocked` here just like the one above.
        return albums
    }
    ///retrieves all the documents in the collection at the path
    public func retrieve<FC : Identifiable & Codable>(path: String) async throws -> [FC]{
        let querySnapshot = try await store.collection(path)
            .getDocuments()
        return try querySnapshot.documents.compactMap { document in
            try document.data(as: FC.self)
        }
    }
}

然后您只需在表示层中使用几行代码即可实现它。

import SwiftUI
@MainActor
class AlbumListViewModel: ObservableObject{
    @Published var albums: [Album] = []
    private let svc = NestedFirestoreService()
    
    func loadAlbums() async throws{
        albums = try await svc.retrieveAlbums()
    }
}
struct AlbumListView: View {
    @StateObject var vm: AlbumListViewModel = .init()

    var body: some View {
        List(vm.albums, id:\.id) { album in
            DisclosureGroup(album.name) {
                ForEach(album.songs ?? [], id:\.id){ song in
                    Text(song.title)
                }
            }
        }.task {
            do{
                try await vm.loadAlbums()
            }catch{
                print(error)
            }
        }
    }
}

struct AlbumListView_Previews: PreviewProvider {
    static var previews: some View {
        AlbumListView()
    }
}

如果遇到任何解码错误,请通过将问号添加到类型中来使变量可选,就像我对数组所做的那样。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程1:获取数据时EXC_BAD_INSTRUCTION 的相关文章

随机推荐