线程1:获取数据时执行EXC_BAD_INSTRUCTION

dgtucam1  于 2022-10-04  发布在  Swift
关注(0)|答案(2)|浏览(181)

我随机得到这个错误->Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)。我不太明白这到底是什么时候发生的。大多数情况下,是在视图刷新时。错误出现在执行group.leave()的行上。

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

我的Firebase数据库有存储为文档的专辑,每个文档都有相册图像/名称和2个子集合“未锁定”的文档(用户uid)存储“已锁定”:Bool和“歌曲”,每个歌曲都有一个文档存储图像/名称

这是获取我的专辑和他们的歌曲的函数:

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工作得很好。我希望得到的答案能解释这段代码如何与Async一起工作,我真的花了很长时间自己弄明白了这一点。此外,我想知道DispatchGroup到底发生了什么,为什么只有一张专辑很好,但不能有多张专辑。

xkrw2x1b

xkrw2x1b1#

我认为你用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()
    }
}

如果遇到任何解码错误,请将变量设置为可选,方法是向类型添加问号,就像我对数组所做的那样。

6rqinv9w

6rqinv9w2#

只需按正确的顺序使用它们:

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

func fetchAlbums() {
    group.enter()
    FirebaseManager.shared.firestore.collection("albums").getDocuments { querySnapshot, error in
        if let error = error {
            print(error.localizedDescription)
            group.leave()
            return
        }
        guard let documents = querySnapshot?.documents else {
            group.leave()
            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

            group.enter()
            FirebaseManager.shared.firestore.collection("albums").document(docID)
                .collection("songs").getDocuments { querySnapshot, error in
                    if let error = error {
                        group.leave()
                        return
                    }
                    guard let documents = querySnapshot?.documents else {
                        group.leave()
                        return
                    }
                    self.albumSongs = documents.compactMap { document -> AlbumSong? in
                        do {
                            group.leave()
                            return try document.data(as: AlbumSong.self)
                        } catch {
                            group.leave()
                            return nil
                        }
                    }
                }

            group.enter()
            FirebaseManager.shared.firestore.collection("albums").document(docID)
                .collection("unlocked").document(uid ?? "").getDocument { docSnapshot, error in
                    if let error = error {
                        group.leave()
                        return
                    }
                    guard let document = docSnapshot?.data() else {
                        group.leave()
                        return
                    }

                    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)
                    }

                    group.leave()
                }
        }

        group.leave()
    }

    group.notify(queue: DispatchQueue.global()) {
        // do your stuff
    }
}

相关问题