SwiftUI: How to dismiss a View after Firebase Firestore document changes?

Viewed 87

TLDR: How do I dismiss the current View presented by a NavigationLink when its userId is no longer in the array anymore.

I am trying to create a "kick out" feature for the owner to kick other users. Still, after setting up a listener for any changes on a Space document, I am not sure how to call presentationMode.wrappedValue.dismiss() for users whose ID is not in Space's array anymore.

SpaceView

    struct SpaceView: View {
        
        @ObservedObject var spaceViewModel: SpaceViewModel
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
        var body: some View {
            ZStack {
                VStack(spacing: 15) {
                    membersCollectionView
                    Spacer()
                }
                .padding(.bottom, 40)
                moreActionSheetView
            }
        }
        
        //MARK: - Views
        var membersCollectionView: some View {
            ScrollView {
                LazyVGrid(
                    columns: spaceViewModel.memberColumns,
                    alignment: .leading,
                    spacing: 16,
                    pinnedViews: [.sectionHeaders, .sectionFooters]
                ) {
                    ForEach(spaceViewModel.space.members, id: \.self) { member in
                        SpaceMemberCell(member: member, action: spaceViewModel.spaceMemberCellTapped(_:))
                    }
                }
                .padding([.leading, .trailing], 20)
            }
        }
    }

SpaceViewModel

    class SpaceViewModel: ObservableObject {
        @Published var space: Space
        
        @Published var showAddFriendsActionSheet = false
        @Published var showMoreButton: Bool = true
        @Published var showMusicButton: Bool = false
        @Published var showAddFriendsButton: Bool = true
        @Published var showRaiseHandButton: Bool = false
        @Published var showMuteButton: Bool = false
        @Published var memberColumns: [GridItem] = []
        @Published var selectedMember: SpaceMember?
        @Published var accessType: SpaceAccessType = .member
        @Published var memberActionSheetOffset: CGFloat = 0
        @Published var moreActionSheetOffset: CGFloat = 0
        @Published private(set) var isAllMuted: Bool
        //@Published private(set) var roomStatus: Space.RoomStatus
        private var spaceListener: ListenerRegistration!
        @Published var isKickedOut = false
        
        init(space: Space) {
            self.space = space
            self.isAllMuted = space.isAllMuted
            self.roomStatus = space.roomStatus
            self.memberColumns = Array(repeating: GridItem(.fixed(100), spacing: 16), count: space.members.count)
            setAccessType()
        }
        
        deinit {
            removeListeners()
        }
        
        ///initializer for personal/solo space
        init(personalSpace: Space) {
            self.space = personalSpace
            self.isAllMuted = personalSpace.isAllMuted
            self.roomStatus = personalSpace.roomStatus
            setAccessType()
            self.showMuteButton = false
        }
        
        func removeListeners() {
            if spaceListener != nil {
                spaceListener.remove()
            }
        }
        
        func setAccessType(){
            guard let userId = Customer.current?.userId else { return }
            if space.owner.memberInfo.userId == userId {
                accessType = .owner
            } else if space.speakerIds.contains(where: { $0 == userId }) { //check if current user is one of the speakerIds
                accessType = .speaker
            }
            switch accessType {
            case .owner:
                showMusicButton = true
                showRaiseHandButton = false
                showMuteButton = true
                
            case .speaker:
                showRaiseHandButton = true
                showMuteButton = true
                showMusicButton = false
                
            case .member:
                showRaiseHandButton = true
                showMuteButton = false
                showMusicButton = false
            }
        }
        
        func updateIsAllMuted(shouldMute: Bool) {
            let newSpace = space
            isAllMuted = shouldMute
            SpaceService.updateIsAllMuted(isAllMuted: shouldMute, spaceId: space.spaceId)
            newSpace.updateIsAllMuted(shouldMute)
            space = newSpace
        }
        
        func startSpaceListener() {
            spaceListener = db.collection(CollectionKeys.spaces)
                .document(space.spaceId)
                .addSnapshotListener({ (snapshot, error) in
                    if let error = error {
                        print("Error on space listener \(error.localizedDescription)")
                        self.handleError(title: "Error fetching space", message: error.localizedDescription)
                        return
                    }
                    guard let snapshot = snapshot,
                          let newSpace = Space(document: snapshot)
                    else {
                        print("Failed to create a space in listener")
                        return
                    }
                    self.space = newSpace
                    let memberIds = newSpace.members.map({$0.userId})
                    if !memberIds.contains(Customer.current!.userId) {
                        **//kick out user here or something**
                    }
                })
        }
        
        func spaceMemberCellTapped(_ member: SpaceMember) {
            selectedMember = member
            memberActionSheetOffset = 0
        }

        func removeFromSpace() {
            guard let member = selectedMember else { return }
            SpaceService.removeMember(member: member, spaceId: space.spaceId) { (alertError) in
                if let alertError = alertError {
                    print("Error toggling space status \(alertError)")
                    //self.handleAlertError(alertError: alertError)
                    return
                }
                self.space.removeMember(member: member)
            }
            selectedMember = nil
            memberActionSheetOffset = UIScreen.main.bounds.height
        }
    }

In SpaceViewModel's startSpaceListener(), I want to dismiss SpaceView if the user's userId is not in the space list anymore. Space, SpaceMember, and SpaceOwner are nothing but a simple class without any decorator.

I don't think it is necessary, but I can provide the model's code upon request.

Thank you :)

1 Answers

I think you need to set isKickedOut to true inside

    if !memberIds.contains(Customer.current!.userId) {
        isKickedOut = true 
    }

Then in this part

    ForEach(spaceViewModel.space.members, id: \.self) { member in
                        SpaceMemberCell(member: member, action: spaceViewModel.spaceMemberCellTapped(_:))
}

check if spaceViewModel.isKickedOut is false and only in this case return SpaceMemberCells

like:

    if !spaceViewModel.isKickedOut {
        ForEach(spaceViewModel.space.members, id: \.self) { member in
                            SpaceMemberCell(member: member, action: spaceViewModel.spaceMemberCellTapped(_:))
    }
 }
Related